Update imgui.

This commit is contained in:
Mr_Goldberg 2021-06-13 00:07:16 -04:00
parent 02195f5636
commit 7350397f9d
No known key found for this signature in database
GPG key ID: 8597D87419DEF278
19 changed files with 496 additions and 233 deletions

View file

@ -1,4 +1,4 @@
// dear imgui, v1.83 WIP
// dear imgui, v1.84 WIP
// (widgets code)
/*
@ -1537,7 +1537,9 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc
//-------------------------------------------------------------------------
// [SECTION] Widgets: ComboBox
//-------------------------------------------------------------------------
// - CalcMaxPopupHeightFromItemCount() [Internal]
// - BeginCombo()
// - BeginComboPopup() [Internal]
// - EndCombo()
// - Combo()
//-------------------------------------------------------------------------
@ -1552,79 +1554,91 @@ static float CalcMaxPopupHeightFromItemCount(int items_count)
bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)
{
// Always consume the SetNextWindowSizeConstraint() call in our early return paths
ImGuiContext& g = *GImGui;
bool has_window_size_constraint = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) != 0;
g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint;
ImGuiWindow* window = GetCurrentWindow();
ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags;
g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values
if (window->SkipItems)
return false;
IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together
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
const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();
const ImVec2 label_size = CalcTextSize(label, NULL, true);
const float expected_w = CalcItemWidth();
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : expected_w;
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));
const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));
const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : 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);
if (!ItemAdd(total_bb, id, &frame_bb))
if (!ItemAdd(total_bb, id, &bb))
return false;
// Open on click
bool hovered, held;
bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
bool popup_open = IsPopupOpen(id, ImGuiPopupFlags_None);
bool pressed = ButtonBehavior(bb, id, &hovered, &held);
const ImGuiID popup_id = ImHashStr("##ComboPopup", 0, id);
bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);
if ((pressed || g.NavActivateId == id) && !popup_open)
{
OpenPopupEx(popup_id, ImGuiPopupFlags_None);
popup_open = true;
}
// Render shape
const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
const float value_x2 = ImMax(frame_bb.Min.x, frame_bb.Max.x - arrow_size);
RenderNavHighlight(frame_bb, id);
const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);
RenderNavHighlight(bb, id);
if (!(flags & ImGuiComboFlags_NoPreview))
window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(value_x2, frame_bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);
window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);
if (!(flags & ImGuiComboFlags_NoArrowButton))
{
ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
ImU32 text_col = GetColorU32(ImGuiCol_Text);
window->DrawList->AddRectFilled(ImVec2(value_x2, frame_bb.Min.y), frame_bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);
if (value_x2 + arrow_size - style.FramePadding.x <= frame_bb.Max.x)
RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);
window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);
if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x)
RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);
}
RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding);
// Render preview and label
if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
{
ImVec2 preview_pos = frame_bb.Min + style.FramePadding;
if (g.LogEnabled)
LogSetNextTextDecoration("{", "}");
RenderTextClipped(preview_pos, ImVec2(value_x2, frame_bb.Max.y), preview_value, NULL, NULL, ImVec2(0.0f, 0.0f));
RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL);
}
if (label_size.x > 0)
RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
if ((pressed || g.NavActivateId == id) && !popup_open)
{
if (window->DC.NavLayerCurrent == 0)
window->NavLastIds[0] = id;
OpenPopupEx(id, ImGuiPopupFlags_None);
popup_open = true;
}
RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);
if (!popup_open)
return false;
if (has_window_size_constraint)
g.NextWindowData.Flags = backup_next_window_data_flags;
return BeginComboPopup(popup_id, bb, flags);
}
bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags)
{
ImGuiContext& g = *GImGui;
if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None))
{
g.NextWindowData.ClearFlags();
return false;
}
// Set popup size
float w = bb.GetWidth();
if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)
{
g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;
g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);
}
else
{
if ((flags & ImGuiComboFlags_HeightMask_) == 0)
flags |= ImGuiComboFlags_HeightRegular;
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one
int popup_max_height_in_items = -1;
if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8;
else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4;
@ -1632,30 +1646,27 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF
SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
}
// This is essentially a specialized version of BeginPopupEx()
char name[16];
ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth
// Position the window given a custom constraint (peak into expected window size so we can position it)
// This might be easier to express with an hypothetical SetNextWindowPosConstraints() function.
// Set position given a custom constraint (peak into expected window size so we can position it)
// FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function?
// FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()?
if (ImGuiWindow* popup_window = FindWindowByName(name))
if (popup_window->WasActive)
{
// Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us.
ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window);
if (flags & ImGuiComboFlags_PopupAlignLeft)
popup_window->AutoPosLastDirection = ImGuiDir_Left; // "Below, Toward Left"
else
popup_window->AutoPosLastDirection = ImGuiDir_Down; // "Below, Toward Right (default)"
ImRect r_outer = GetWindowAllowedExtentRect(popup_window);
ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = "Below, Toward Left", Down = "Below, Toward Right (default)"
ImRect r_outer = GetPopupAllowedExtentRect(popup_window);
ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox);
SetNextWindowPos(pos);
}
// We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx()
ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove;
// Horizontally align ourselves with the framed text
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y));
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text
bool ret = Begin(name, NULL, window_flags);
PopStyleVar();
if (!ret)
@ -3579,12 +3590,12 @@ static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* t
namespace ImStb
{
static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; }
static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->TextW[idx]; }
static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* 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 = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }
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 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 = *GImGui; 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';
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx)
static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)
{
const ImWchar* text = obj->TextW.Data;
const ImWchar* text_remaining = NULL;
@ -3597,19 +3608,20 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* ob
r->num_chars = (int)(text_remaining - (text + line_start_idx));
}
// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators.
static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; }
static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }
#ifdef __APPLE__ // FIXME: Move setting to IO structure
static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx]) ) : 1; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }
#else
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }
#endif
#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h
#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL
static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)
{
ImWchar* dst = obj->TextW.Data + pos;
@ -3625,9 +3637,9 @@ static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n)
*dst = '\0';
}
static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len)
static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len)
{
const bool is_resizable = (obj->UserFlags & ImGuiInputTextFlags_CallbackResize) != 0;
const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;
const int text_len = obj->CurLenW;
IM_ASSERT(pos <= text_len);
@ -3681,7 +3693,7 @@ static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const Im
// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling
// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)
static void stb_textedit_replace(STB_TEXTEDIT_STRING* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len)
static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len)
{
stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);
ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);
@ -4067,7 +4079,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
backup_current_text_length = state->CurLenA;
state->Edited = false;
state->BufCapacityA = buf_size;
state->UserFlags = flags;
state->Flags = flags;
state->UserCallback = callback;
state->UserCallbackData = callback_user_data;
@ -4420,7 +4432,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
}
// Clear temporary user storage
state->UserFlags = 0;
state->Flags = ImGuiInputTextFlags_None;
state->UserCallback = NULL;
state->UserCallbackData = NULL;
}
@ -6297,7 +6309,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
// Plot/Graph widgets are not very good.
// Consider writing your own, or using a third-party one, see:
// - ImPlot https://github.com/epezent/implot
// - others https://github.com/ocornut/imgui/wiki/Useful-Widgets
// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions
//-------------------------------------------------------------------------
int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size)
@ -6882,11 +6894,11 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo
if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)
{
// 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 render neither the shortcut neither the selected tick mark
// 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);
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));
pressed = Selectable(label, false, flags, ImVec2(w, 0.0f));
pressed = Selectable(label, selected, flags, ImVec2(w, 0.0f));
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().
}
@ -7762,7 +7774,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open,
tab->Flags = flags;
// Append name with zero-terminator
tab->NameOffset = (ImS16)tab_bar->TabsNames.size();
tab->NameOffset = (ImS32)tab_bar->TabsNames.size();
tab_bar->TabsNames.append(label, label + strlen(label) + 1);
// Update selected tab