ImGuiTextFilter: fixed filtering when mixing "include" and "-exclude" keyword so that an "-exclude" match always filters out. (#2435)

+ fix typo in ImToUpper
This commit is contained in:
ocornut 2026-09-18 22:02:57 +02:00
parent fc98dc4a72
commit 54f86be71e
4 changed files with 37 additions and 40 deletions

View File

@ -84,6 +84,8 @@ Other Changes:
hasn't been owned by another items. This is more correct and also necessary
to avoid Drags/Sliders cancelling also triggering a cancel menu. (#8564, #9534)
- ImGuiTextFilter:
- Fixed filtering when mixing "include" and "-exclude" keyword so that an "-exclude"
match always filters out. (#2435) [@kudaba, @ocornut]
- Added `DrawWithHint()` function. (#6206, #6395, #6447)
- Added a "incl,-excl" hint by default when the search field is empty. (#6206, #6395, #6447)
- Style:

View File

@ -3116,74 +3116,69 @@ bool ImGuiTextFilter::DrawWithHint(const char* label, const char* hint)
return value_changed;
}
static void ImStrSplit(const char* b, const char* e, char separator, ImVector<ImGuiTextFilter::ImGuiTextFilterItem>* out)
static void ImGuiTextFilter_BuildAddItem(ImGuiTextFilter* f, const char* word_b, const char* word_e)
{
out->resize(0);
const char* wb = b;
const char* we = wb;
while (we < e)
{
if (*we == separator)
{
out->push_back(ImGuiTextFilter::ImGuiTextFilterItem(wb, we));
wb = we + 1;
}
we++;
}
if (wb != we)
out->push_back(ImGuiTextFilter::ImGuiTextFilterItem(wb, we));
// Trim (FIXME: UTF-8 support?)
while (word_b < word_e && ImCharIsBlankA(word_b[0]))
word_b++;
while (word_e > word_b && ImCharIsBlankA(word_e[-1]))
word_e--;
if (word_e - word_b <= 0)
return;
// Add to list
// FIXME-OPT: on push_front(): as N is derived from user inputs we expect this to be fine.
const bool is_excl = (word_b[0] == '-');
f->_Items.insert(is_excl ? f->_Items.Data : f->_Items.end(), ImGuiTextFilter::ImGuiTextFilterItem(word_b, word_e));
if (!is_excl)
f->_CountInclude++;
}
void ImGuiTextFilter::Build()
{
// Parse filters
_Items.resize(0);
ImGuiTextFilterItem input_range(InputBuf, InputBuf + ImStrlen(InputBuf));
ImStrSplit(InputBuf, InputBuf + ImStrlen(InputBuf), ',', &_Items);
_CountInclude = 0;
for (ImGuiTextFilterItem& f : _Items)
const char* buf_e = InputBuf + ImStrlen(InputBuf);
const char* word_b = InputBuf;
const char* word_e = word_b;
while (word_e < buf_e)
{
while (f.Begin < f.End && ImCharIsBlankA(f.Begin[0]))
f.Begin++;
while (f.End > f.Begin && ImCharIsBlankA(f.End[-1]))
f.End--;
if (f.Begin == f.End)
continue;
if (f.Begin[0] != '-')
_CountInclude += 1;
if (*word_e == ',')
{
ImGuiTextFilter_BuildAddItem(this, word_b, word_e);
word_b = word_e + 1;
}
word_e++;
}
ImGuiTextFilter_BuildAddItem(this, word_b, word_e);
}
bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const
{
if (_Items.Size == 0)
return true;
if (text == NULL)
text = text_end = "";
for (const ImGuiTextFilterItem& f : _Items)
// Filters are sorted so that '-' ones are always leading.
for (const ImGuiTextFilterItem& item : _Items)
{
if (f.Begin == f.End)
continue;
if (f.Begin[0] == '-')
if (item.Begin[0] == '-')
{
// Subtract
if (ImStristr(text, text_end, f.Begin + 1, f.End) != NULL)
if (ImStristr(text, text_end, item.Begin + 1, item.End) != NULL) // Exclude
return false;
}
else
{
// Grep
if (ImStristr(text, text_end, f.Begin, f.End) != NULL)
if (ImStristr(text, text_end, item.Begin, item.End) != NULL) // Include
return true;
}
}
// Implicit * grep
// When no inclusion are specified (only exclusions) we implicitly pass
if (_CountInclude == 0)
return true;
return false;
}

View File

@ -2812,7 +2812,7 @@ struct ImGuiTextFilter
// [Internal] Members
char InputBuf[256]; // User input buffer
int _CountInclude; // >= 0
ImVector<ImGuiTextFilterItem> _Items; // Pre-parsed, trimmed items
ImVector<ImGuiTextFilterItem> _Items; // Pre-parsed, trimmed, reordered items
};
// Helper: Growable text buffer for logging/accumulating text

View File

@ -407,7 +407,7 @@ IMGUI_API const char* ImStrSkipBlank(const char* str);
IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string)
IMGUI_API const char* ImStrbol(const char* buf_mid_line, const char* buf_begin); // Find beginning-of-line
IM_MSVC_RUNTIME_CHECKS_OFF
inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }
inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c & ~32 : c; }
inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; }
inline bool ImCharIsXdigitA(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); }