text: #include "imgui_searchablecombo.h"
  #include "imgui_internal.h"
  
  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);
  }
  
  /* Modified version of BeginCombo from imgui.cpp at line 9172,
   * to include a input field to be able to filter the combo values. */
  bool ImGui::BeginSearchableCombo(const char* label, const char* preview_value, char* input, int input_size, const char* input_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();
      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 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));
  
      bool hovered, held;
      bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held);
      bool popup_open = IsPopupOpen(id);
  
      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);
  
      if (popup_open || (pressed || g.NavActivateId == id))
      {
          ImGuiID activeId = g.ActiveId;
          if (popup_open)
              g.ActiveId = id;
          else
              g.NavInputId = id;
          InputTextEx(label, input_preview_value, input, input_size, frame_bb.Max - frame_bb.Min, ImGuiInputTextFlags_NoUndoRedo);
          g.ActiveId = activeId;
          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) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
              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);
          }
      }
      else
      {
          ItemSize(total_bb, style.FramePadding.y);
          if (!ItemAdd(total_bb, id, &frame_bb))
              return false;
      }
  
      if ((pressed || g.NavActivateId == id) && !popup_open)
      {
          if (window->DC.NavLayerCurrent == 0)
              window->NavLastIds[0] = id;
          OpenPopupEx(id);
          popup_open = true;
      }
  
      if (!popup_open)
      {
          RenderNavHighlight(frame_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) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Left);
          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) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right);
              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);
          }
          RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding);
  
          if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))
              RenderTextClipped(frame_bb.Min + style.FramePadding, ImVec2(value_x2, frame_bb.Max.y), 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);
  
          return false;
      }
  
      if (has_window_size_constraint)
      {
          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
          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.BeginPopupStack.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);
          }
  
      // 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));
      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;
  }
  
  
  // Just so you can end your BeginSearchableCombo with EndSearchableCombo.
  void ImGui::EndSearchableCombo()
  {
      EndCombo();
  }
  
  
  /* Modified version of Combo from imgui.cpp at line 9343,
   * to include a input field to be able to filter the combo values. */
  bool ImGui::SearchableCombo(const char* label, int* current_item, std::vector<std::string> items, const char* default_preview_text, const char* input_preview_value, int popup_max_height_in_items)
  {
      ImGuiContext& g = *GImGui;
  
      const char* preview_text = NULL;
      if (*current_item >= (int)items.size())
          *current_item = 0;
      if (*current_item >= 0 && *current_item < (int)items.size())
          preview_text = items[*current_item].c_str();
      else
          preview_text = default_preview_text;
  
      // 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))
          SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));
  
      const int input_size = 64;
      char input_buffer[input_size] = "";
      if (!BeginSearchableCombo(label, preview_text, input_buffer, input_size, input_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)
      int matched_items = 0;
      bool value_changed = false;
      for (int i = 0; i < (int)items.size(); i++)
      {
          char buffer[input_size] = "";
          ImStrncpy(buffer, input_buffer, input_size);
          std::string input(buffer);
          std::string item = items[i];
  
          std::transform(item.begin(), item.end(), item.begin(),
              [](unsigned char c) { return (unsigned char)std::tolower(c); });
          std::transform(input.begin(), input.end(), input.begin(),
              [](unsigned char c) { return (unsigned char)std::tolower(c); });
  
          if (item.find(input, 0) == std::string::npos)
              continue;
  
          matched_items++;
          PushID((void*)(intptr_t)i);
          const bool item_selected = (i == *current_item);
          const char* item_text = items[i].c_str();
          if (Selectable(item_text, item_selected))
          {
              value_changed = true;
              *current_item = i;
          }
          if (item_selected)
              SetItemDefaultFocus();
          PopID();
      }
      if (matched_items == 0)
          ImGui::Selectable("No maps found", false, ImGuiSelectableFlags_Disabled);
  
      EndSearchableCombo();
  
      return value_changed;
  }