Bladeren bron

ImStrv: convert Combo()/ListBox() to use ImStrv in their getters.

Didn't convert Combo() "const char* items_separated_by_zeros" as this doesn't translate well when used via a ImStrv constructor, may actually aim to obsolete.
Fixed for shadowing local variable warning on 2025/06/25.
ocornut 1 jaar geleden
bovenliggende
commit
a2d0f4df78
3 gewijzigde bestanden met toevoegingen van 68 en 22 verwijderingen
  1. 6 2
      imgui.h
  2. 1 1
      imgui_demo.cpp
  3. 61 19
      imgui_widgets.cpp

+ 6 - 2
imgui.h

@@ -677,8 +677,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.
@@ -804,7 +805,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!
@@ -4061,6 +4063,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.92.0 (from June 2025)
     inline void         PushFont(ImFont* font)                                  { PushFont(font, font ? font->LegacySize : 0.0f); }
     IMGUI_API void      SetWindowFontScale(float scale);                        // Set font scale factor for current window. Prefer using PushFont(NULL, style.FontSizeBase * factor) or use style.FontScaleMain to scale all windows.

+ 1 - 1
imgui_demo.cpp

@@ -1369,7 +1369,7 @@ static void DemoWindowWidgetsComboBoxes()
 
         // Simplified one-liner Combo() using an accessor function
         static int item_current_4 = 0;
-        ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items));
+        ImGui::Combo("combo 5 (function)", &item_current_4, [](void* data, int n) { return ImStrv(((const char**)data)[n]); }, items, IM_ARRAYSIZE(items));
 
         ImGui::TreePop();
     }

+ 61 - 19
imgui_widgets.cpp

@@ -2096,15 +2096,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;
@@ -2112,20 +2118,20 @@ static const char* Items_SingleStringGetter(void* data, int idx)
     while (*p)
     {
         if (idx == items_count)
-            break;
+            return p;
         p += ImStrlen(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);
 
@@ -2144,8 +2150,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);
@@ -2168,24 +2174,46 @@ 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, ImStrv const items[], int items_count, int height_in_items)
+{
+    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)
 {
-    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_CharArrayGetter, (void*)items, items_count, height_in_items);
+}
+
+#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
+struct GetterProxyData { void* UserData; const char* (*UserGetter)(void* user_data, int idx); };
+static ImStrv ConstCharToStrvProxy(void* user_data, int idx)
+{
+    GetterProxyData* p_proxy_data = (GetterProxyData*)user_data;
+    return ImStrv(p_proxy_data->UserGetter(p_proxy_data->UserData, idx));
+}
+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.
+    GetterProxyData proxy_data = { user_data, getter };
+    return Combo(label, current_item, ConstCharToStrvProxy, &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 += ImStrlen(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
@@ -8613,15 +8641,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;
 
@@ -8643,8 +8676,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);
@@ -8666,6 +8699,15 @@ 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.
+    GetterProxyData proxy_data = { user_data, getter };
+    return ListBox(label, current_item, ConstCharToStrvProxy, &proxy_data, items_count, height_in_items);
+}
+#endif
+
 //-------------------------------------------------------------------------
 // [SECTION] Widgets: PlotLines, PlotHistogram
 //-------------------------------------------------------------------------