|
@@ -10732,213 +10732,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla
|
|
|
return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags);
|
|
|
}
|
|
|
|
|
|
-static float CalcMaxPopupHeightFromItemCount(int items_count)
|
|
|
-{
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
- if (items_count <= 0)
|
|
|
- return FLT_MAX;
|
|
|
- return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond;
|
|
|
- g.NextWindowData.SizeConstraintCond = 0;
|
|
|
-
|
|
|
- ImGuiWindow* window = GetCurrentWindow();
|
|
|
- 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);
|
|
|
-
|
|
|
- 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 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));
|
|
|
- ItemSize(total_bb, style.FramePadding.y);
|
|
|
- if (!ItemAdd(total_bb, id, &frame_bb))
|
|
|
- return false;
|
|
|
-
|
|
|
- bool hovered, held;
|
|
|
- bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
|
|
|
- bool popup_open = IsPopupOpen(id);
|
|
|
-
|
|
|
- const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f));
|
|
|
- const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
|
|
- RenderNavHighlight(frame_bb, id);
|
|
|
- if (!(flags & ImGuiComboFlags_NoPreview))
|
|
|
- window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left);
|
|
|
- if (!(flags & ImGuiComboFlags_NoArrowButton))
|
|
|
- {
|
|
|
- window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
|
|
|
- RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down);
|
|
|
- }
|
|
|
- RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
|
|
|
- if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
|
|
|
- RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f));
|
|
|
- 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);
|
|
|
- popup_open = true;
|
|
|
- }
|
|
|
-
|
|
|
- if (!popup_open)
|
|
|
- return false;
|
|
|
-
|
|
|
- if (backup_next_window_size_constraint)
|
|
|
- {
|
|
|
- g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint;
|
|
|
- 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
|
|
|
- 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;
|
|
|
- else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20;
|
|
|
- SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
|
|
|
- }
|
|
|
-
|
|
|
- char name[16];
|
|
|
- ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth
|
|
|
-
|
|
|
- // Peak into expected window size so we can position it
|
|
|
- if (ImGuiWindow* popup_window = FindWindowByName(name))
|
|
|
- if (popup_window->WasActive)
|
|
|
- {
|
|
|
- ImVec2 size_expected = CalcWindowExpectedSize(popup_window);
|
|
|
- if (flags & ImGuiComboFlags_PopupAlignLeft)
|
|
|
- popup_window->AutoPosLastDirection = ImGuiDir_Left;
|
|
|
- ImRect r_outer = GetWindowAllowedExtentRect(popup_window);
|
|
|
- ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox);
|
|
|
- SetNextWindowPos(pos);
|
|
|
- }
|
|
|
-
|
|
|
- // Horizontally align ourselves with the framed text
|
|
|
- ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;
|
|
|
- PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y));
|
|
|
- bool ret = Begin(name, NULL, window_flags);
|
|
|
- PopStyleVar();
|
|
|
- if (!ret)
|
|
|
- {
|
|
|
- EndPopup();
|
|
|
- IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above
|
|
|
- return false;
|
|
|
- }
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void ImGui::EndCombo()
|
|
|
-{
|
|
|
- EndPopup();
|
|
|
-}
|
|
|
-
|
|
|
-// Getter for the old Combo() API: const char*[]
|
|
|
-static bool Items_ArrayGetter(void* data, int idx, const char** out_text)
|
|
|
-{
|
|
|
- const char* const* items = (const char* const*)data;
|
|
|
- if (out_text)
|
|
|
- *out_text = items[idx];
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-// Getter for the old Combo() API: "item1\0item2\0item3\0"
|
|
|
-static bool Items_SingleStringGetter(void* data, int idx, const char** out_text)
|
|
|
-{
|
|
|
- // 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;
|
|
|
- while (*p)
|
|
|
- {
|
|
|
- if (idx == items_count)
|
|
|
- break;
|
|
|
- p += strlen(p) + 1;
|
|
|
- items_count++;
|
|
|
- }
|
|
|
- if (!*p)
|
|
|
- return false;
|
|
|
- if (out_text)
|
|
|
- *out_text = p;
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-// 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)
|
|
|
-{
|
|
|
- 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);
|
|
|
-
|
|
|
- // 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.SizeConstraintCond)
|
|
|
- SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
|
|
|
-
|
|
|
- if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))
|
|
|
- return false;
|
|
|
-
|
|
|
- // Display items
|
|
|
- // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)
|
|
|
- bool value_changed = false;
|
|
|
- for (int i = 0; i < items_count; i++)
|
|
|
- {
|
|
|
- PushID((void*)(intptr_t)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))
|
|
|
- {
|
|
|
- value_changed = true;
|
|
|
- *current_item = i;
|
|
|
- }
|
|
|
- if (item_selected)
|
|
|
- SetItemDefaultFocus();
|
|
|
- PopID();
|
|
|
- }
|
|
|
-
|
|
|
- EndCombo();
|
|
|
- return value_changed;
|
|
|
-}
|
|
|
-
|
|
|
-// Combo box helper allowing to pass an array of strings.
|
|
|
-bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)
|
|
|
-{
|
|
|
- const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);
|
|
|
- return value_changed;
|
|
|
-}
|
|
|
-
|
|
|
-// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0"
|
|
|
-bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)
|
|
|
-{
|
|
|
- int items_count = 0;
|
|
|
- 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;
|
|
|
- items_count++;
|
|
|
- }
|
|
|
- bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);
|
|
|
- return value_changed;
|
|
|
-}
|
|
|
-
|
|
|
// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
|
|
|
// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
|
|
|
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
|
|
@@ -11042,108 +10835,6 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-// FIXME: Rename to BeginListBox()
|
|
|
-// Helper to calculate the size of a listbox and display a label on the right.
|
|
|
-// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty"
|
|
|
-bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg)
|
|
|
-{
|
|
|
- ImGuiWindow* window = GetCurrentWindow();
|
|
|
- if (window->SkipItems)
|
|
|
- return false;
|
|
|
-
|
|
|
- const ImGuiStyle& style = GetStyle();
|
|
|
- const ImGuiID id = GetID(label);
|
|
|
- const ImVec2 label_size = CalcTextSize(label, NULL, true);
|
|
|
-
|
|
|
- // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
|
|
|
- ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y);
|
|
|
- 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));
|
|
|
- window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy.
|
|
|
-
|
|
|
- BeginGroup();
|
|
|
- if (label_size.x > 0)
|
|
|
- RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);
|
|
|
-
|
|
|
- BeginChildFrame(id, frame_bb.GetSize());
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-// FIXME: Rename to BeginListBox()
|
|
|
-bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items)
|
|
|
-{
|
|
|
- // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.
|
|
|
- // We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size.
|
|
|
- // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution.
|
|
|
- if (height_in_items < 0)
|
|
|
- height_in_items = ImMin(items_count, 7);
|
|
|
- float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f);
|
|
|
-
|
|
|
- // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild().
|
|
|
- ImVec2 size;
|
|
|
- size.x = 0.0f;
|
|
|
- size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y;
|
|
|
- return ListBoxHeader(label, size);
|
|
|
-}
|
|
|
-
|
|
|
-// FIXME: Rename to EndListBox()
|
|
|
-void ImGui::ListBoxFooter()
|
|
|
-{
|
|
|
- ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow;
|
|
|
- const ImRect bb = parent_window->DC.LastItemRect;
|
|
|
- const ImGuiStyle& style = GetStyle();
|
|
|
-
|
|
|
- EndChildFrame();
|
|
|
-
|
|
|
- // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect)
|
|
|
- // We call SameLine() to restore DC.CurrentLine* data
|
|
|
- SameLine();
|
|
|
- parent_window->DC.CursorPos = bb.Min;
|
|
|
- ItemSize(bb, style.FramePadding.y);
|
|
|
- EndGroup();
|
|
|
-}
|
|
|
-
|
|
|
-bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)
|
|
|
-{
|
|
|
- const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);
|
|
|
- return value_changed;
|
|
|
-}
|
|
|
-
|
|
|
-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)
|
|
|
-{
|
|
|
- if (!ListBoxHeader(label, items_count, height_in_items))
|
|
|
- return false;
|
|
|
-
|
|
|
- // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
|
|
|
- ImGuiContext& g = *GImGui;
|
|
|
- bool value_changed = false;
|
|
|
- ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
|
|
|
- while (clipper.Step())
|
|
|
- for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
|
|
- {
|
|
|
- const bool item_selected = (i == *current_item);
|
|
|
- const char* item_text;
|
|
|
- if (!items_getter(data, i, &item_text))
|
|
|
- item_text = "*Unknown item*";
|
|
|
-
|
|
|
- PushID(i);
|
|
|
- if (Selectable(item_text, item_selected))
|
|
|
- {
|
|
|
- *current_item = i;
|
|
|
- value_changed = true;
|
|
|
- }
|
|
|
- if (item_selected)
|
|
|
- SetItemDefaultFocus();
|
|
|
- PopID();
|
|
|
- }
|
|
|
- ListBoxFooter();
|
|
|
- if (value_changed)
|
|
|
- MarkItemEdited(g.CurrentWindow->DC.LastItemId);
|
|
|
-
|
|
|
- return value_changed;
|
|
|
-}
|
|
|
-
|
|
|
bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|