diff --git a/imgui.h b/imgui.h index d3b15bcd8..8cd102b61 100644 --- a/imgui.h +++ b/imgui.h @@ -595,8 +595,9 @@ namespace ImGui IMGUI_API bool BeginCombo(ImStrv label, ImStrv preview_value, ImGuiComboFlags flags = 0); IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! IMGUI_API bool Combo(ImStrv label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); + IMGUI_API bool Combo(ImStrv label, int* current_item, ImStrv const items[], int items_count, int popup_max_height_in_items = -1); + IMGUI_API bool Combo(ImStrv label, int* current_item, ImStrv (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1); IMGUI_API bool Combo(ImStrv label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" - IMGUI_API bool Combo(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. @@ -721,7 +722,8 @@ namespace ImGui IMGUI_API bool BeginListBox(ImStrv label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region IMGUI_API void EndListBox(); // only call EndListBox() if BeginListBox() returned true! IMGUI_API bool ListBox(ImStrv label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); - IMGUI_API bool ListBox(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(ImStrv label, int* current_item, ImStrv const items[], int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(ImStrv label, int* current_item, ImStrv (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1); // Widgets: Data Plotting // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! @@ -3643,6 +3645,8 @@ namespace ImGui inline ImGuiID GetID(const char* str_id_begin, const char* str_id_end) { return GetID(ImStrv(str_id_begin, str_id_end)); } inline void TextUnformatted(const char* text, const char* text_end) { TextUnformatted(ImStrv(text, text_end)); } inline ImVec2 CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash = false, float wrap_width = -1.0f) { return CalcTextSize(ImStrv(text, text_end), hide_text_after_double_hash, wrap_width); } + IMGUI_API bool Combo(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1); + IMGUI_API bool ListBox(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1); // OBSOLETED in 1.91.0 (from July 2024) static inline void PushButtonRepeat(bool repeat) { PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); } static inline void PopButtonRepeat() { PopItemFlag(); } diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 849339984..b0047167b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1455,7 +1455,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) // Simplified one-liner Combo() using an accessor function static int item_current_4 = 0; - ImGui::Combo("combo 4 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 4 (function)", &item_current_4, [](void* data, int n) { return ImStrv(((const char**)data)[n]); }, items, IM_ARRAYSIZE(items)); ImGui::TreePop(); } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0641e6bf6..0205377c0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -2016,15 +2016,21 @@ void ImGui::EndComboPreview() preview_data->PreviewRect = ImRect(); } -// Getter for the old Combo() API: const char*[] -static const char* Items_ArrayGetter(void* data, int idx) +// Getter for the old Combo()/ListBox() API: const char*[] +static ImStrv Items_CharArrayGetter(void* data, int idx) { const char* const* items = (const char* const*)data; return items[idx]; } +static ImStrv Items_StrvArrayGetter(void* data, int idx) +{ + ImStrv const* items = (ImStrv const*)data; + return items[idx]; +} + // Getter for the old Combo() API: "item1\0item2\0item3\0" -static const char* Items_SingleStringGetter(void* data, int idx) +static ImStrv Items_SingleStringGetter(void* data, int idx) { const char* items_separated_by_zeros = (const char*)data; int items_count = 0; @@ -2032,20 +2038,20 @@ static const char* Items_SingleStringGetter(void* data, int idx) while (*p) { if (idx == items_count) - break; + return p; p += strlen(p) + 1; items_count++; } - return *p ? p : NULL; + return NULL; } // Old API, prefer using BeginCombo() nowadays if you can. -bool ImGui::Combo(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items) +bool ImGui::Combo(ImStrv label, int* current_item, ImStrv (*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; + ImStrv preview_value = NULL; if (*current_item >= 0 && *current_item < items_count) preview_value = getter(user_data, *current_item); @@ -2064,8 +2070,8 @@ bool ImGui::Combo(ImStrv label, int* current_item, const char* (*getter)(void* u while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const char* item_text = getter(user_data, i); - if (item_text == NULL) + ImStrv item_text = getter(user_data, i); + if (!item_text) item_text = "*Unknown item*"; PushID(i); @@ -2088,24 +2094,41 @@ bool ImGui::Combo(ImStrv label, int* current_item, const char* (*getter)(void* u } // Combo box helper allowing to pass an array of strings. -bool ImGui::Combo(ImStrv label, int* current_item, const char* const items[], int items_count, int height_in_items) +bool ImGui::Combo(ImStrv label, int* current_item, ImStrv 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; + return Combo(label, current_item, Items_StrvArrayGetter, (void*)items, items_count, height_in_items); } +// We cannot easily obsolete the 'const char* []' version as this would be stored on user side.. +bool ImGui::Combo(ImStrv label, int* current_item, const char* const items[], int items_count, int height_in_items) +{ + return Combo(label, current_item, Items_CharArrayGetter, (void*)items, items_count, height_in_items); +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +bool ImGui::Combo(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items) +{ + // Convert 'const char*' getter to ImStrv version via a proxy. Cumbersome but no way out. + struct GetterProxyData { void* UserData; const char* (*UserGetter)(void* user_data, int idx); }; + GetterProxyData proxy_data = { user_data, getter }; + return Combo(label, current_item, [](void* user_data, int idx) { GetterProxyData& proxy_data = *(GetterProxyData*)user_data; return ImStrv(proxy_data.UserGetter(proxy_data.UserData, idx)); }, &proxy_data, items_count, popup_max_height_in_items); +} +#endif + // Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items "item1\0item2\0" +// We don't support an ImStrv version of this, because passing the typical "hello\0world" to a ImStrv constructor wouldn't yield the expected result. +// FIXME-OPT: Avoid computing count, or at least only when combo is open +// FIXME-OPT: The dual pass is stupid, at least could build an index on first pass. bool ImGui::Combo(ImStrv 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 + const char* p = items_separated_by_zeros; 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; + return Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -8265,15 +8288,20 @@ void ImGui::EndListBox() EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label } +bool ImGui::ListBox(ImStrv label, int* current_item, ImStrv const items[], int items_count, int height_items) +{ + return ListBox(label, current_item, Items_StrvArrayGetter, (void*)items, items_count, height_items); +} + +// We cannot easily obsolete the 'const char* []' version as this would be stored on user side.. bool ImGui::ListBox(ImStrv 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; + return ListBox(label, current_item, Items_CharArrayGetter, (void*)items, items_count, height_items); } // This is merely a helper around BeginListBox(), EndListBox(). // Considering using those directly to submit custom data or store selection differently. -bool ImGui::ListBox(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) +bool ImGui::ListBox(ImStrv label, int* current_item, ImStrv (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) { ImGuiContext& g = *GImGui; @@ -8295,8 +8323,8 @@ bool ImGui::ListBox(ImStrv label, int* current_item, const char* (*getter)(void* while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const char* item_text = getter(user_data, i); - if (item_text == NULL) + ImStrv item_text = getter(user_data, i); + if (!item_text) item_text = "*Unknown item*"; PushID(i); @@ -8318,6 +8346,16 @@ bool ImGui::ListBox(ImStrv label, int* current_item, const char* (*getter)(void* return value_changed; } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +bool ImGui::ListBox(ImStrv label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) +{ + // Convert 'const char*' getter to ImStrv version via a proxy. Cumbersome but no way out. + struct GetterProxyData { void* UserData; const char* (*UserGetter)(void* user_data, int idx); }; + GetterProxyData proxy_data = { user_data, getter }; + return ListBox(label, current_item, [](void* user_data, int idx) { GetterProxyData& proxy_data = *(GetterProxyData*)user_data; return ImStrv(proxy_data.UserGetter(proxy_data.UserData, idx)); }, &proxy_data, items_count, height_in_items); +} +#endif + //------------------------------------------------------------------------- // [SECTION] Widgets: PlotLines, PlotHistogram //-------------------------------------------------------------------------