update Dear ImGui to v1.90
bunch of changes
This commit is contained in:
parent
b3d60e512c
commit
74c4a52413
50 changed files with 2859 additions and 1321 deletions
487
extern/imgui_patched/imgui_widgets.cpp
vendored
487
extern/imgui_patched/imgui_widgets.cpp
vendored
|
|
@ -1,4 +1,4 @@
|
|||
// dear imgui, v1.89.9
|
||||
// dear imgui, v1.90.0
|
||||
// (widgets code)
|
||||
|
||||
/*
|
||||
|
|
@ -18,6 +18,8 @@ Index of this file:
|
|||
// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.
|
||||
// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.
|
||||
// [SECTION] Widgets: Selectable
|
||||
// [SECTION] Widgets: Typing-Select support
|
||||
// [SECTION] Widgets: Multi-Select support
|
||||
// [SECTION] Widgets: ListBox
|
||||
// [SECTION] Widgets: PlotLines, PlotHistogram
|
||||
// [SECTION] Widgets: Value helpers
|
||||
|
|
@ -994,7 +996,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)
|
|||
ImRect bb_interact = bb;
|
||||
const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea();
|
||||
if (area_to_visible_ratio < 1.5f)
|
||||
bb_interact.Expand(ImFloor(bb_interact.GetSize() * -0.25f));
|
||||
bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f));
|
||||
|
||||
// Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window.
|
||||
// (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer).
|
||||
|
|
@ -1048,7 +1050,7 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_no
|
|||
|
||||
// Switch to moving the window after mouse is moved beyond the initial drag threshold
|
||||
if (IsItemActive() && IsMouseDragging(0))
|
||||
StartMouseMovingWindowOrNode(window, dock_node, true);
|
||||
StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
|
@ -1130,7 +1132,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6
|
|||
const bool allow_interaction = (alpha >= 1.0f);
|
||||
|
||||
ImRect bb = bb_frame;
|
||||
bb.Expand(ImVec2(-ImClamp(IM_FLOOR((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_FLOOR((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
|
||||
bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));
|
||||
|
||||
// V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)
|
||||
const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();
|
||||
|
|
@ -1225,7 +1227,7 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2&
|
|||
|
||||
// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390)
|
||||
// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API.
|
||||
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
|
||||
bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID 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;
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
|
|
@ -1233,7 +1235,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size
|
|||
return false;
|
||||
|
||||
const ImVec2 padding = g.Style.FramePadding;
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2.0f);
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);
|
||||
ItemSize(bb);
|
||||
if (!ItemAdd(bb, id))
|
||||
return false;
|
||||
|
|
@ -1252,14 +1254,15 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size
|
|||
return pressed;
|
||||
}
|
||||
|
||||
bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
|
||||
// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.
|
||||
bool ImGui::ImageButton(const char* str_id, 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 = g.CurrentWindow;
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
return ImageButtonEx(window->GetID(str_id), user_texture_id, size, uv0, uv1, bg_col, tint_col);
|
||||
return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);
|
||||
}
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
|
@ -1326,12 +1329,12 @@ bool ImGui::Checkbox(const char* label, bool* v)
|
|||
{
|
||||
// Undocumented tristate/mixed/indeterminate checkbox (#2644)
|
||||
// This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox)
|
||||
ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)));
|
||||
ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)));
|
||||
window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);
|
||||
}
|
||||
else if (*v)
|
||||
{
|
||||
const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
|
||||
const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
|
||||
RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);
|
||||
}
|
||||
|
||||
|
|
@ -1427,7 +1430,7 @@ bool ImGui::RadioButton(const char* label, bool active)
|
|||
window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);
|
||||
if (active)
|
||||
{
|
||||
const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
|
||||
const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));
|
||||
window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));
|
||||
}
|
||||
|
||||
|
|
@ -1607,26 +1610,19 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness)
|
|||
else if (flags & ImGuiSeparatorFlags_Horizontal)
|
||||
{
|
||||
// Horizontal Separator
|
||||
float x1 = window->Pos.x;
|
||||
float x2 = window->Pos.x + window->Size.x;
|
||||
|
||||
// FIXME-WORKRECT: old hack (#205) until we decide of consistent behavior with WorkRect/Indent and Separator
|
||||
if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID)
|
||||
x1 += window->DC.Indent.x;
|
||||
|
||||
// FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want,
|
||||
// need to introduce a variant of WorkRect for that purpose. (#4787)
|
||||
if (ImGuiTable* table = g.CurrentTable)
|
||||
{
|
||||
x1 = table->Columns[table->CurrentColumn].MinX;
|
||||
x2 = table->Columns[table->CurrentColumn].MaxX;
|
||||
}
|
||||
float x1 = window->DC.CursorPos.x;
|
||||
float x2 = window->WorkRect.Max.x;
|
||||
|
||||
// Preserve legacy behavior inside Columns()
|
||||
// Before Tables API happened, we relied on Separator() to span all columns of a Columns() set.
|
||||
// We currently don't need to provide the same feature for tables because tables naturally have border features.
|
||||
ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL;
|
||||
if (columns)
|
||||
{
|
||||
x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03
|
||||
x2 = window->Pos.x + window->Size.x;
|
||||
PushColumnsBackground();
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
|
@ -1660,7 +1656,11 @@ void ImGui::Separator()
|
|||
// Those flags should eventually be configurable by the user
|
||||
// FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f.
|
||||
ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;
|
||||
flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot.
|
||||
|
||||
// Only applies to legacy Columns() api as they relied on Separator() a lot.
|
||||
if (window->DC.CurrentColumns)
|
||||
flags |= ImGuiSeparatorFlags_SpanAllColumns;
|
||||
|
||||
SeparatorEx(flags, 1.0f);
|
||||
}
|
||||
|
||||
|
|
@ -1677,14 +1677,14 @@ 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 = ImFloor((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;
|
||||
|
||||
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 seps_y = ImTrunc((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
|
||||
|
|
@ -1759,8 +1759,7 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
|
|||
ImRect bb_render = bb;
|
||||
if (held)
|
||||
{
|
||||
ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min;
|
||||
float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x;
|
||||
float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis];
|
||||
|
||||
// Minimum pane size
|
||||
float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);
|
||||
|
|
@ -1773,12 +1772,8 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float
|
|||
// Apply resize
|
||||
if (mouse_delta != 0.0f)
|
||||
{
|
||||
if (mouse_delta < 0.0f)
|
||||
IM_ASSERT(*size1 + mouse_delta >= min_size1);
|
||||
if (mouse_delta > 0.0f)
|
||||
IM_ASSERT(*size2 - mouse_delta >= min_size2);
|
||||
*size1 += mouse_delta;
|
||||
*size2 -= mouse_delta;
|
||||
*size1 = ImMax(*size1 + mouse_delta, min_size1);
|
||||
*size2 = ImMax(*size2 - mouse_delta, min_size2);
|
||||
bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));
|
||||
MarkItemEdited(id);
|
||||
}
|
||||
|
|
@ -1832,7 +1827,7 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc
|
|||
width_excess = 0.0f;
|
||||
for (int n = 0; n < count; n++)
|
||||
{
|
||||
float width_rounded = ImFloor(items[n].Width);
|
||||
float width_rounded = ImTrunc(items[n].Width);
|
||||
width_excess += items[n].Width - width_rounded;
|
||||
items[n].Width = width_rounded;
|
||||
}
|
||||
|
|
@ -1878,10 +1873,13 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
|
|||
const ImGuiStyle& style = g.Style;
|
||||
const ImGuiID id = window->GetID(label);
|
||||
IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together
|
||||
if (flags & ImGuiComboFlags_WidthFitPreview)
|
||||
IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | ImGuiComboFlags_CustomPreview)) == 0);
|
||||
|
||||
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
|
||||
const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
||||
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth();
|
||||
const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;
|
||||
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
|
||||
const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
ItemSize(total_bb, style.FramePadding.y);
|
||||
|
|
@ -2063,18 +2061,15 @@ void ImGui::EndComboPreview()
|
|||
}
|
||||
|
||||
// Getter for the old Combo() API: const char*[]
|
||||
static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
|
||||
static const char* Items_ArrayGetter(void* data, int idx)
|
||||
{
|
||||
const char* const* items = (const char* const*)data;
|
||||
if (out_text)
|
||||
*out_text = items[idx];
|
||||
return true;
|
||||
return items[idx];
|
||||
}
|
||||
|
||||
// Getter for the old Combo() API: "item1\0item2\0item3\0"
|
||||
static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
|
||||
static const char* Items_SingleStringGetter(void* data, int idx)
|
||||
{
|
||||
// FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited.
|
||||
const char* items_separated_by_zeros = (const char*)data;
|
||||
int items_count = 0;
|
||||
const char* p = items_separated_by_zeros;
|
||||
|
|
@ -2085,22 +2080,18 @@ static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
|
|||
p += strlen(p) + 1;
|
||||
items_count++;
|
||||
}
|
||||
if (!*p)
|
||||
return false;
|
||||
if (out_text)
|
||||
*out_text = p;
|
||||
return true;
|
||||
return *p ? p : NULL;
|
||||
}
|
||||
|
||||
// Old API, prefer using BeginCombo() nowadays if you can.
|
||||
bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items)
|
||||
bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
// Call the getter to obtain the preview string which is a parameter to BeginCombo()
|
||||
const char* preview_value = NULL;
|
||||
if (*current_item >= 0 && *current_item < items_count)
|
||||
items_getter(data, *current_item, &preview_value);
|
||||
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))
|
||||
|
|
@ -2114,11 +2105,12 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
|
|||
bool value_changed = false;
|
||||
for (int i = 0; i < items_count; i++)
|
||||
{
|
||||
const char* item_text = getter(user_data, i);
|
||||
if (item_text == NULL)
|
||||
item_text = "*Unknown item*";
|
||||
|
||||
PushID(i);
|
||||
const bool item_selected = (i == *current_item);
|
||||
const char* item_text;
|
||||
if (!items_getter(data, i, &item_text))
|
||||
item_text = "*Unknown item*";
|
||||
if (Selectable(item_text, item_selected) && *current_item != i)
|
||||
{
|
||||
value_changed = true;
|
||||
|
|
@ -2158,6 +2150,30 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
|
|||
return value_changed;
|
||||
}
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
|
||||
struct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); };
|
||||
static const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx)
|
||||
{
|
||||
ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data;
|
||||
const char* s = NULL;
|
||||
data->OldCallback(data->UserData, idx, &s);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items)
|
||||
{
|
||||
ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
|
||||
return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items);
|
||||
}
|
||||
bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items)
|
||||
{
|
||||
ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };
|
||||
return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Data Type and Data Formatting Helpers [Internal]
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
@ -4132,7 +4148,7 @@ namespace ImStb
|
|||
{
|
||||
|
||||
static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; }
|
||||
static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; }
|
||||
static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; }
|
||||
static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }
|
||||
static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; }
|
||||
static ImWchar STB_TEXTEDIT_NEWLINE = '\n';
|
||||
|
|
@ -4573,8 +4589,13 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||
item_data_backup = g.LastItemData;
|
||||
window->DC.CursorPos = backup_pos;
|
||||
|
||||
// Prevent NavActivate reactivating in BeginChild().
|
||||
const ImGuiID backup_activate_id = g.NavActivateId;
|
||||
if (g.ActiveId == id) // Prevent reactivation
|
||||
g.NavActivateId = 0;
|
||||
|
||||
// We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.
|
||||
// FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre.
|
||||
// TODO: test. is this necessary?
|
||||
if(!style.DoFrameShadingForMultilineText)
|
||||
{
|
||||
PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);
|
||||
|
|
@ -4584,6 +4605,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||
PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);
|
||||
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges
|
||||
bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove);
|
||||
g.NavActivateId = backup_activate_id;
|
||||
PopStyleVar(3);
|
||||
|
||||
if(!style.DoFrameShadingForMultilineText)
|
||||
|
|
@ -5327,9 +5349,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||
const float scroll_increment_x = inner_size.x * 0.25f;
|
||||
const float visible_width = inner_size.x - style.FramePadding.x;
|
||||
if (cursor_offset.x < state->ScrollX)
|
||||
state->ScrollX = IM_FLOOR(ImMax(0.0f, cursor_offset.x - scroll_increment_x));
|
||||
state->ScrollX = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x));
|
||||
else if (cursor_offset.x - visible_width >= state->ScrollX)
|
||||
state->ScrollX = IM_FLOOR(cursor_offset.x - visible_width + scroll_increment_x);
|
||||
state->ScrollX = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -5380,7 +5402,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||
{
|
||||
float selection_word_wrap = has_word_wrap ? (word_wrap_width - ImMax(0.0f, rect_pos.x - draw_pos.x)) : 0.0f;
|
||||
ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true, selection_word_wrap, false);
|
||||
if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
|
||||
if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines
|
||||
ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));
|
||||
rect.ClipWith(clip_rect);
|
||||
if (rect.Overlaps(clip_rect))
|
||||
|
|
@ -5403,7 +5425,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
|
|||
{
|
||||
state->CursorAnim += io.DeltaTime;
|
||||
bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;
|
||||
ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll);
|
||||
ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);
|
||||
ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);
|
||||
if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))
|
||||
draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));
|
||||
|
|
@ -5488,7 +5510,7 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)
|
|||
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
|
||||
if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), ImGuiChildFlags_Border)) // Visualize undo state
|
||||
{
|
||||
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
for (int n = 0; n < STB_TEXTEDIT_UNDOSTATECOUNT; n++)
|
||||
|
|
@ -5633,8 +5655,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag
|
|||
if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)
|
||||
{
|
||||
// RGB/HSV 0..255 Sliders
|
||||
const float w_item_one = ImMax(1.0f, IM_FLOOR((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
|
||||
const float w_item_last = ImMax(1.0f, IM_FLOOR(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
|
||||
const float w_item_one = ImMax(1.0f, IM_TRUNC((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));
|
||||
const float w_item_last = ImMax(1.0f, IM_TRUNC(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));
|
||||
|
||||
const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x);
|
||||
static const char* ids[4] = { "##X", "##Y", "##Z", "##W" };
|
||||
|
|
@ -5878,7 +5900,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|||
float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box
|
||||
float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;
|
||||
float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;
|
||||
float bars_triangles_half_sz = IM_FLOOR(bars_width * 0.20f);
|
||||
float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f);
|
||||
|
||||
float backup_initial_col[4];
|
||||
memcpy(backup_initial_col, col, components * sizeof(float));
|
||||
|
|
@ -6046,7 +6068,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl
|
|||
if ((flags & ImGuiColorEditFlags_NoInputs) == 0)
|
||||
{
|
||||
PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);
|
||||
ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
|
||||
ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;
|
||||
ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;
|
||||
if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)
|
||||
if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))
|
||||
|
|
@ -6602,36 +6624,52 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
|||
|
||||
// We vertically grow up to current line height up the typical widget height.
|
||||
const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);
|
||||
const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL);
|
||||
ImRect frame_bb;
|
||||
frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
|
||||
frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
|
||||
frame_bb.Min.y = window->DC.CursorPos.y;
|
||||
frame_bb.Max.x = window->WorkRect.Max.x;
|
||||
frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;
|
||||
frame_bb.Max.y = window->DC.CursorPos.y + frame_height;
|
||||
if (display_frame)
|
||||
{
|
||||
// Framed header expand a little outside the default padding, to the edge of InnerClipRect
|
||||
// (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f)
|
||||
frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f);
|
||||
frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f);
|
||||
frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f);
|
||||
frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f);
|
||||
}
|
||||
|
||||
const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapser arrow width + Spacing
|
||||
const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing
|
||||
const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
|
||||
const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser
|
||||
const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapsing
|
||||
ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
|
||||
ItemSize(ImVec2(text_width, frame_height), padding.y);
|
||||
|
||||
// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing
|
||||
ImRect interact_bb = frame_bb;
|
||||
if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth)) == 0)
|
||||
if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)
|
||||
interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f;
|
||||
|
||||
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
|
||||
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
|
||||
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
|
||||
if (span_all_columns)
|
||||
{
|
||||
window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
|
||||
window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
|
||||
}
|
||||
|
||||
// Compute open and multi-select states before ItemAdd() as it clear NextItem data.
|
||||
bool is_open = TreeNodeUpdateNextOpen(id, flags);
|
||||
bool item_add = ItemAdd(interact_bb, id);
|
||||
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
|
||||
g.LastItemData.DisplayRect = frame_bb;
|
||||
|
||||
if (span_all_columns)
|
||||
{
|
||||
window->ClipRect.Min.x = backup_clip_rect_min_x;
|
||||
window->ClipRect.Max.x = backup_clip_rect_max_x;
|
||||
}
|
||||
|
||||
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:
|
||||
// Store data for the current depth to allow returning to this node from any child item.
|
||||
// For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().
|
||||
|
|
@ -6657,6 +6695,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
|||
return is_open;
|
||||
}
|
||||
|
||||
if (span_all_columns)
|
||||
TablePushBackgroundChannel();
|
||||
|
||||
ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
|
||||
if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap))
|
||||
button_flags |= ImGuiButtonFlags_AllowOverlap;
|
||||
|
|
@ -6757,7 +6798,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
|||
|
||||
if (g.LogEnabled)
|
||||
LogSetNextTextDecoration("###", "###");
|
||||
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -6774,9 +6814,17 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
|||
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);
|
||||
if (g.LogEnabled)
|
||||
LogSetNextTextDecoration(">", NULL);
|
||||
RenderText(text_pos, label, label_end, false);
|
||||
}
|
||||
|
||||
if (span_all_columns)
|
||||
TablePopBackgroundChannel();
|
||||
|
||||
// Label
|
||||
if (display_frame)
|
||||
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
|
||||
else
|
||||
RenderText(text_pos, label, label_end, false);
|
||||
|
||||
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
|
||||
TreePushOverrideID(id);
|
||||
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));
|
||||
|
|
@ -6953,8 +7001,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|||
{
|
||||
const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;
|
||||
const float spacing_y = style.ItemSpacing.y;
|
||||
const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
|
||||
const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
|
||||
const float spacing_L = IM_TRUNC(spacing_x * 0.50f);
|
||||
const float spacing_U = IM_TRUNC(spacing_y * 0.50f);
|
||||
bb.Min.x -= spacing_L;
|
||||
bb.Min.y -= spacing_U;
|
||||
bb.Max.x += (spacing_x - spacing_L);
|
||||
|
|
@ -6962,7 +7010,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|||
}
|
||||
//if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
|
||||
|
||||
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable..
|
||||
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
|
||||
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
|
||||
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
|
||||
if (span_all_columns)
|
||||
|
|
@ -7077,6 +7125,212 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Widgets: Typing-Select support
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// [Experimental] Currently not exposed in public API.
|
||||
// Consume character inputs and return search request, if any.
|
||||
// This would typically only be called on the focused window or location you want to grab inputs for, e.g.
|
||||
// if (ImGui::IsWindowFocused(...))
|
||||
// if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest())
|
||||
// focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1);
|
||||
// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer).
|
||||
ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTypingSelectState* data = &g.TypingSelectState;
|
||||
ImGuiTypingSelectRequest* out_request = &data->Request;
|
||||
|
||||
// Clear buffer
|
||||
const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config.
|
||||
const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times
|
||||
if (data->SearchBuffer[0] != 0)
|
||||
{
|
||||
bool clear_buffer = false;
|
||||
clear_buffer |= (g.NavFocusScopeId != data->FocusScope);
|
||||
clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time);
|
||||
clear_buffer |= g.NavAnyRequest;
|
||||
clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere
|
||||
clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter);
|
||||
clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;
|
||||
//if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); }
|
||||
if (clear_buffer)
|
||||
data->Clear();
|
||||
}
|
||||
|
||||
// Append to buffer
|
||||
const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;
|
||||
int buffer_len = (int)strlen(data->SearchBuffer);
|
||||
bool select_request = false;
|
||||
for (ImWchar w : g.IO.InputQueueCharacters)
|
||||
{
|
||||
const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1);
|
||||
if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks
|
||||
continue;
|
||||
char w_buf[5];
|
||||
ImTextCharToUtf8(w_buf, (unsigned int)w);
|
||||
if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)
|
||||
{
|
||||
select_request = true; // Same character: don't need to append to buffer.
|
||||
continue;
|
||||
}
|
||||
if (data->SingleCharModeLock)
|
||||
{
|
||||
data->Clear(); // Different character: clear
|
||||
buffer_len = 0;
|
||||
}
|
||||
memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append
|
||||
buffer_len += w_len;
|
||||
select_request = true;
|
||||
}
|
||||
g.IO.InputQueueCharacters.resize(0);
|
||||
|
||||
// Handle backspace
|
||||
if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat))
|
||||
{
|
||||
char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);
|
||||
*p = 0;
|
||||
buffer_len = (int)(p - data->SearchBuffer);
|
||||
}
|
||||
|
||||
// Return request if any
|
||||
if (buffer_len == 0)
|
||||
return NULL;
|
||||
if (select_request)
|
||||
{
|
||||
data->FocusScope = g.NavFocusScopeId;
|
||||
data->LastRequestFrame = g.FrameCount;
|
||||
data->LastRequestTime = (float)g.Time;
|
||||
}
|
||||
out_request->Flags = flags;
|
||||
out_request->SearchBufferLen = buffer_len;
|
||||
out_request->SearchBuffer = data->SearchBuffer;
|
||||
out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount);
|
||||
out_request->SingleCharMode = false;
|
||||
out_request->SingleCharSize = 0;
|
||||
|
||||
// Calculate if buffer contains the same character repeated.
|
||||
// - This can be used to implement a special search mode on first character.
|
||||
// - Performed on UTF-8 codepoint for correctness.
|
||||
// - SingleCharMode is always set for first input character, because it usually leads to a "next".
|
||||
if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode)
|
||||
{
|
||||
const char* buf_begin = out_request->SearchBuffer;
|
||||
const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen;
|
||||
const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end);
|
||||
const char* p = buf_begin + c0_len;
|
||||
for (; p < buf_end; p += c0_len)
|
||||
if (memcmp(buf_begin, p, (size_t)c0_len) != 0)
|
||||
break;
|
||||
const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;
|
||||
out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock);
|
||||
out_request->SingleCharSize = (ImS8)c0_len;
|
||||
data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode.
|
||||
}
|
||||
|
||||
return out_request;
|
||||
}
|
||||
|
||||
static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2)
|
||||
{
|
||||
int match_len = 0;
|
||||
while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++))
|
||||
match_len++;
|
||||
return match_len;
|
||||
}
|
||||
|
||||
// Default handler for finding a result for typing-select. You may implement your own.
|
||||
// You might want to display a tooltip to visualize the current request SearchBuffer
|
||||
// When SingleCharMode is set:
|
||||
// - it is better to NOT display a tooltip of other on-screen display indicator.
|
||||
// - the index of the currently focused item is required.
|
||||
// if your SetNextItemSelectionData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.
|
||||
int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
|
||||
{
|
||||
if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot.
|
||||
return -1;
|
||||
int idx = -1;
|
||||
if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))
|
||||
idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx);
|
||||
else
|
||||
idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);
|
||||
if (idx != -1)
|
||||
NavRestoreHighlightAfterMove();
|
||||
return idx;
|
||||
}
|
||||
|
||||
// Special handling when a single character is repeated: perform search on a single letter and goes to next.
|
||||
int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
|
||||
{
|
||||
// FIXME: Assume selection user data is index. Would be extremely practical.
|
||||
//if (nav_item_idx == -1)
|
||||
// nav_item_idx = (int)g.NavLastValidSelectionUserData;
|
||||
|
||||
int first_match_idx = -1;
|
||||
bool return_next_match = false;
|
||||
for (int idx = 0; idx < items_count; idx++)
|
||||
{
|
||||
const char* item_name = get_item_name_func(user_data, idx);
|
||||
if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize)
|
||||
continue;
|
||||
if (return_next_match) // Return next matching item after current item.
|
||||
return idx;
|
||||
if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value.
|
||||
return idx;
|
||||
if (first_match_idx == -1) // Record first match for wrapping.
|
||||
first_match_idx = idx;
|
||||
if (nav_item_idx == idx) // Record that we encountering nav_item so we can return next match.
|
||||
return_next_match = true;
|
||||
}
|
||||
return first_match_idx; // First result
|
||||
}
|
||||
|
||||
int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data)
|
||||
{
|
||||
int longest_match_idx = -1;
|
||||
int longest_match_len = 0;
|
||||
for (int idx = 0; idx < items_count; idx++)
|
||||
{
|
||||
const char* item_name = get_item_name_func(user_data, idx);
|
||||
const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name);
|
||||
if (match_len <= longest_match_len)
|
||||
continue;
|
||||
longest_match_idx = idx;
|
||||
longest_match_len = match_len;
|
||||
if (match_len == req->SearchBufferLen)
|
||||
break;
|
||||
}
|
||||
return longest_match_idx;
|
||||
}
|
||||
|
||||
void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)
|
||||
{
|
||||
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||
Text("SearchBuffer = \"%s\"", data->SearchBuffer);
|
||||
Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock);
|
||||
Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame);
|
||||
#else
|
||||
IM_UNUSED(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Widgets: Multi-Select support
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)
|
||||
{
|
||||
// Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!
|
||||
// This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.
|
||||
ImGuiContext& g = *GImGui;
|
||||
g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;
|
||||
g.NextItemData.SelectionUserData = selection_user_data;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// [SECTION] Widgets: ListBox
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
@ -7085,6 +7339,7 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
|
|||
// - ListBox()
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.
|
||||
// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty"
|
||||
// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).
|
||||
bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
|
||||
|
|
@ -7100,7 +7355,7 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
|
|||
|
||||
// Size default to hold ~7.25 items.
|
||||
// Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
|
||||
ImVec2 size = ImFloor(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));
|
||||
ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));
|
||||
ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));
|
||||
ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
|
||||
ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
|
||||
|
|
@ -7110,10 +7365,11 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
|
|||
{
|
||||
ItemSize(bb.GetSize(), style.FramePadding.y);
|
||||
ItemAdd(bb, 0, &frame_bb);
|
||||
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME-OPT: We could omit the BeginGroup() if label_size.x but would need to omit the EndGroup() as well.
|
||||
// FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well.
|
||||
BeginGroup();
|
||||
if (label_size.x > 0.0f)
|
||||
{
|
||||
|
|
@ -7122,7 +7378,7 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)
|
|||
window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);
|
||||
}
|
||||
|
||||
BeginChildFrame(id, frame_bb.GetSize());
|
||||
BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -7133,7 +7389,7 @@ void ImGui::EndListBox()
|
|||
IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?");
|
||||
IM_UNUSED(window);
|
||||
|
||||
EndChildFrame();
|
||||
EndChild();
|
||||
EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label
|
||||
}
|
||||
|
||||
|
|
@ -7145,7 +7401,7 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* const item
|
|||
|
||||
// This is merely a helper around BeginListBox(), EndListBox().
|
||||
// Considering using those directly to submit custom data or store selection differently.
|
||||
bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items)
|
||||
bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
|
||||
|
|
@ -7153,7 +7409,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
|
|||
if (height_in_items < 0)
|
||||
height_in_items = ImMin(items_count, 7);
|
||||
float height_in_items_f = height_in_items + 0.25f;
|
||||
ImVec2 size(0.0f, ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
|
||||
ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
|
||||
|
||||
if (!BeginListBox(label, size))
|
||||
return false;
|
||||
|
|
@ -7166,8 +7422,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
|
|||
while (clipper.Step())
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
||||
{
|
||||
const char* item_text;
|
||||
if (!items_getter(data, i, &item_text))
|
||||
const char* item_text = getter(user_data, i);
|
||||
if (item_text == NULL)
|
||||
item_text = "*Unknown item*";
|
||||
|
||||
PushID(i);
|
||||
|
|
@ -7514,12 +7770,18 @@ void ImGui::EndMenuBar()
|
|||
PopClipRect();
|
||||
PopID();
|
||||
window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.
|
||||
g.GroupStack.back().EmitItem = false;
|
||||
EndGroup(); // Restore position on layer 0
|
||||
|
||||
// FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here.
|
||||
ImGuiGroupData& group_data = g.GroupStack.back();
|
||||
group_data.EmitItem = false;
|
||||
ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos;
|
||||
window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent.
|
||||
EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore
|
||||
window->DC.LayoutType = ImGuiLayoutType_Vertical;
|
||||
window->DC.IsSameLine = false;
|
||||
window->DC.NavLayerCurrent = ImGuiNavLayer_Main;
|
||||
window->DC.MenuBarAppending = false;
|
||||
window->DC.CursorMaxPos = restore_cursor_max_pos;
|
||||
}
|
||||
|
||||
// Important: calling order matters!
|
||||
|
|
@ -7679,15 +7941,15 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
|||
// Menu inside an 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_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());
|
||||
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
|
||||
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
float w = label_size.x;
|
||||
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
|
||||
pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y));
|
||||
RenderText(text_pos, label);
|
||||
PopStyleVar();
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -7696,7 +7958,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
|||
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
||||
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
|
||||
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
|
||||
float checkmark_w = IM_FLOOR(g.FontSize * 1.20f);
|
||||
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
|
||||
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame
|
||||
float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
|
||||
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
|
||||
|
|
@ -7743,7 +8005,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)
|
|||
// The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)
|
||||
// But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.
|
||||
// (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)
|
||||
if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover)
|
||||
if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover && g.ActiveId == 0)
|
||||
want_close = true;
|
||||
|
||||
// Open
|
||||
|
|
@ -7873,14 +8135,14 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
|||
// Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful
|
||||
// Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.
|
||||
float w = label_size.x;
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f);
|
||||
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);
|
||||
ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
|
||||
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
|
||||
pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f));
|
||||
PopStyleVar();
|
||||
if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)
|
||||
RenderText(text_pos, label);
|
||||
window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -7889,7 +8151,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut
|
|||
// Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
||||
float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;
|
||||
float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;
|
||||
float checkmark_w = IM_FLOOR(g.FontSize * 1.20f);
|
||||
float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);
|
||||
float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame
|
||||
float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
|
||||
pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));
|
||||
|
|
@ -8036,16 +8298,19 @@ bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)
|
|||
ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);
|
||||
ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);
|
||||
tab_bar->ID = id;
|
||||
return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused, NULL);
|
||||
tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f);
|
||||
tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f);
|
||||
return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused);
|
||||
}
|
||||
|
||||
bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node)
|
||||
bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
IM_ASSERT(tab_bar->ID != 0);
|
||||
if ((flags & ImGuiTabBarFlags_DockNode) == 0)
|
||||
PushOverrideID(tab_bar->ID);
|
||||
|
||||
|
|
@ -8089,19 +8354,12 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG
|
|||
window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);
|
||||
|
||||
// Draw separator
|
||||
// (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable)
|
||||
const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive);
|
||||
const float y = tab_bar->BarRect.Max.y - 1.0f;
|
||||
if (dock_node != NULL)
|
||||
if (g.Style.TabBarBorderSize > 0.0f)
|
||||
{
|
||||
const float separator_min_x = dock_node->Pos.x + window->WindowBorderSize;
|
||||
const float separator_max_x = dock_node->Pos.x + dock_node->Size.x - window->WindowBorderSize;
|
||||
window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float separator_min_x = tab_bar->BarRect.Min.x - IM_FLOOR(window->WindowPadding.x * 0.5f);
|
||||
const float separator_max_x = tab_bar->BarRect.Max.x + IM_FLOOR(window->WindowPadding.x * 0.5f);
|
||||
window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f);
|
||||
const float y = tab_bar->BarRect.Max.y;
|
||||
window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -8308,7 +8566,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)
|
|||
for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)
|
||||
{
|
||||
ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];
|
||||
float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width);
|
||||
float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width);
|
||||
if (shrinked_width < 0.0f)
|
||||
continue;
|
||||
|
||||
|
|
@ -8939,7 +9197,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|||
const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;
|
||||
size.x = tab->Width;
|
||||
if (is_central_section)
|
||||
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
|
||||
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f);
|
||||
else
|
||||
window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);
|
||||
ImVec2 pos = window->DC.CursorPos;
|
||||
|
|
@ -9011,7 +9269,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|||
}
|
||||
|
||||
// Extract a Dockable window out of it's tab bar
|
||||
if (docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove))
|
||||
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);
|
||||
|
|
@ -9048,7 +9307,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
|
|||
if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth)
|
||||
{
|
||||
// Enlarge tab display when hovering
|
||||
bb.Max.x = bb.Min.x + IM_FLOOR(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));
|
||||
bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));
|
||||
display_draw_list = GetForegroundDrawList(window);
|
||||
TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));
|
||||
}
|
||||
|
|
@ -9156,7 +9415,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI
|
|||
IM_ASSERT(width > 0.0f);
|
||||
const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));
|
||||
const float y1 = bb.Min.y + 1.0f;
|
||||
const float y2 = bb.Max.y + ((flags & ImGuiTabItemFlags_Preview) ? 0.0f : -1.0f);
|
||||
const float y2 = bb.Max.y - g.Style.TabBarBorderSize;
|
||||
draw_list->PathLineTo(ImVec2(bb.Min.x, y2));
|
||||
draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);
|
||||
draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);
|
||||
|
|
@ -9235,7 +9494,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
|
|||
}
|
||||
else if (unsaved_marker_visible)
|
||||
{
|
||||
const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f);
|
||||
const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz));
|
||||
RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue