6180: Implement exact matches in search and exclude strings r=Robyt3 a=def-

Thanks to bencie for discussion

<!-- What is the motivation for the changes of this pull request? -->

<!-- Note that builds and other checks will be run for your change. Don't feel intimidated by failures in some of the checks. If you can't resolve them yourself, experienced devs can also resolve them before merging your pull request. -->

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Dennis Felsing <dennis@felsin9.de>
This commit is contained in:
bors[bot] 2023-01-03 18:16:03 +00:00 committed by GitHub
commit 6d3af742dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 90 additions and 89 deletions

View file

@ -46,6 +46,16 @@ public:
bool operator()(int a, int b) { return (g_Config.m_BrSortOrder ? (m_pThis->*m_pfnSort)(b, a) : (m_pThis->*m_pfnSort)(a, b)); }
};
bool matchesPart(const char *a, const char *b)
{
return str_utf8_find_nocase(a, b) != nullptr;
}
bool matchesExactly(const char *a, const char *b)
{
return str_comp(a, &b[1]) == 0;
}
CServerBrowser::CServerBrowser()
{
m_ppServerlist = nullptr;
@ -311,9 +321,16 @@ void CServerBrowser::Filter()
{
continue;
}
auto MatchesFn = matchesPart;
const int FilterLen = str_length(aFilterStr);
if(aFilterStr[0] == '"' && aFilterStr[FilterLen - 1] == '"')
{
aFilterStr[FilterLen - 1] = '\0';
MatchesFn = matchesExactly;
}
// match against server name
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aName, aFilterStr))
if(MatchesFn(m_ppServerlist[i]->m_Info.m_aName, aFilterStr))
{
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
}
@ -321,8 +338,8 @@ void CServerBrowser::Filter()
// match against players
for(int p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++)
{
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, aFilterStr) ||
str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, aFilterStr))
if(MatchesFn(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, aFilterStr) ||
MatchesFn(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, aFilterStr))
{
if(g_Config.m_BrFilterConnectingPlayers &&
str_comp(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, "(connecting)") == 0 &&
@ -336,7 +353,7 @@ void CServerBrowser::Filter()
}
// match against map
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, aFilterStr))
if(MatchesFn(m_ppServerlist[i]->m_Info.m_aMap, aFilterStr))
{
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
}
@ -356,23 +373,30 @@ void CServerBrowser::Filter()
{
continue;
}
auto MatchesFn = matchesPart;
const int FilterLen = str_length(aExcludeStr);
if(aExcludeStr[0] == '"' && aExcludeStr[FilterLen - 1] == '"')
{
aExcludeStr[FilterLen - 1] = '\0';
MatchesFn = matchesExactly;
}
// match against server name
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aName, aExcludeStr))
if(MatchesFn(m_ppServerlist[i]->m_Info.m_aName, aExcludeStr))
{
Filtered = true;
break;
}
// match against map
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, aExcludeStr))
if(MatchesFn(m_ppServerlist[i]->m_Info.m_aMap, aExcludeStr))
{
Filtered = true;
break;
}
// match against gametype
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, aExcludeStr))
if(MatchesFn(m_ppServerlist[i]->m_Info.m_aGameType, aExcludeStr))
{
Filtered = true;
break;

View file

@ -551,6 +551,8 @@ protected:
void RenderServerbrowserFriends(CUIRect View);
void PopupConfirmRemoveFriend();
void RenderServerbrowser(CUIRect MainView);
template<typename F>
bool PrintHighlighted(const char *pName, F &&PrintFn);
static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);

View file

@ -346,30 +346,13 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
bool Printed = false;
if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_SERVERNAME))
{
const char *pStrToken = g_Config.m_BrFilterString;
char aFilterStr[sizeof(g_Config.m_BrFilterString)];
while((pStrToken = str_next_token(pStrToken, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr))))
{
if(aFilterStr[0] == '\0')
{
continue;
}
// highlight the parts that matches
const char *pStr = str_utf8_find_nocase(pItem->m_aName, aFilterStr);
if(pStr)
{
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 0), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pStr - pItem->m_aName));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 1), &Button, pStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)str_length(aFilterStr), &pItem->m_pUIElement->Rect(gs_OffsetColName + 0)->m_Cursor);
TextRender()->TextColor(1, 1, 1, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 2), &Button, pStr + str_length(aFilterStr), FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Rect(gs_OffsetColName + 1)->m_Cursor);
Printed = true;
break;
}
}
}
Printed = PrintHighlighted(pItem->m_aName, [this, pItem, FontSize, &Button](const char *pFilteredStr, const int FilterLen) {
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 0), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pFilteredStr - pItem->m_aName));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 1), &Button, pFilteredStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, FilterLen, &pItem->m_pUIElement->Rect(gs_OffsetColName + 0)->m_Cursor);
TextRender()->TextColor(1, 1, 1, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName + 2), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Rect(gs_OffsetColName + 1)->m_Cursor);
});
if(!Printed)
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColName), &Button, pItem->m_aName, FontSize, TEXTALIGN_LEFT, Button.w, 1, true);
}
@ -390,27 +373,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
float FontSize = 12.0f;
bool Printed = false;
if(g_Config.m_BrFilterString[0] && (pItem->m_QuickSearchHit & IServerBrowser::QUICK_MAPNAME))
{
const char *pStrToken = g_Config.m_BrFilterString;
char aFilterStr[sizeof(g_Config.m_BrFilterString)];
while((pStrToken = str_next_token(pStrToken, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr))))
{
// highlight the parts that matches
const char *pStr = str_utf8_find_nocase(pItem->m_aMap, aFilterStr);
if(pStr)
{
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 0), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pStr - pItem->m_aMap));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 1), &Button, pStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)str_length(aFilterStr), &pItem->m_pUIElement->Rect(gs_OffsetColMap + 0)->m_Cursor);
TextRender()->TextColor(1, 1, 1, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 2), &Button, pStr + str_length(aFilterStr), FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Rect(gs_OffsetColMap + 1)->m_Cursor);
Printed = true;
break;
}
}
}
Printed = PrintHighlighted(pItem->m_aMap, [this, pItem, FontSize, &Button](const char *pFilteredStr, const int FilterLen) {
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 0), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, (int)(pFilteredStr - pItem->m_aMap));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 1), &Button, pFilteredStr, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, FilterLen, &pItem->m_pUIElement->Rect(gs_OffsetColMap + 0)->m_Cursor);
TextRender()->TextColor(1, 1, 1, 1);
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap + 2), &Button, pFilteredStr + FilterLen, FontSize, TEXTALIGN_LEFT, Button.w, 1, true, -1, &pItem->m_pUIElement->Rect(gs_OffsetColMap + 1)->m_Cursor);
});
if(!Printed)
UI()->DoLabelStreamed(*pItem->m_pUIElement->Rect(gs_OffsetColMap), &Button, pItem->m_aMap, FontSize, TEXTALIGN_LEFT, Button.w, 1, true);
}
@ -1215,25 +1185,13 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
const char *pName = pSelectedServer->m_aClients[i].m_aName;
bool Printed = false;
if(g_Config.m_BrFilterString[0])
{
const char *pStr = g_Config.m_BrFilterString;
char aFilterStr[sizeof(g_Config.m_BrFilterString)];
while((pStr = str_next_token(pStr, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr))))
{
// highlight the parts that matches
const char *pFilteredStr = str_utf8_find_nocase(pName, aFilterStr);
if(pFilteredStr)
{
TextRender()->TextEx(&Cursor, pName, (int)(pFilteredStr - pName));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr, str_length(aFilterStr));
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr + str_length(aFilterStr), -1);
Printed = true;
break;
}
}
}
Printed = PrintHighlighted(pName, [this, &Cursor, pName](const char *pFilteredStr, const int FilterLen) {
TextRender()->TextEx(&Cursor, pName, (int)(pFilteredStr - pName));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1);
});
if(!Printed)
TextRender()->TextEx(&Cursor, pName, -1);
@ -1243,25 +1201,13 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
const char *pClan = pSelectedServer->m_aClients[i].m_aClan;
Printed = false;
if(g_Config.m_BrFilterString[0])
{
const char *pStr = g_Config.m_BrFilterString;
char aFilterStr[sizeof(g_Config.m_BrFilterString)];
while((pStr = str_next_token(pStr, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr))))
{
// highlight the parts that matches
const char *pFilteredString = str_utf8_find_nocase(pClan, aFilterStr);
if(pFilteredString)
{
TextRender()->TextEx(&Cursor, pClan, (int)(pFilteredString - pClan));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredString, str_length(aFilterStr));
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredString + str_length(aFilterStr), -1);
Printed = true;
break;
}
}
}
Printed = PrintHighlighted(pClan, [this, &Cursor, pClan](const char *pFilteredStr, const int FilterLen) {
TextRender()->TextEx(&Cursor, pClan, (int)(pFilteredStr - pClan));
TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr, FilterLen);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
TextRender()->TextEx(&Cursor, pFilteredStr + FilterLen, -1);
});
if(!Printed)
TextRender()->TextEx(&Cursor, pClan, -1);
@ -1274,6 +1220,35 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
}
}
template<typename F>
bool CMenus::PrintHighlighted(const char *pName, F &&PrintFn)
{
const char *pStr = g_Config.m_BrFilterString;
char aFilterStr[sizeof(g_Config.m_BrFilterString)];
while((pStr = str_next_token(pStr, IServerBrowser::SEARCH_EXCLUDE_TOKEN, aFilterStr, sizeof(aFilterStr))))
{
// highlight the parts that matches
const char *pFilteredStr;
int FilterLen = str_length(aFilterStr);
if(aFilterStr[0] == '"' && aFilterStr[FilterLen - 1] == '"')
{
aFilterStr[FilterLen - 1] = '\0';
pFilteredStr = str_comp(pName, &aFilterStr[1]) == 0 ? pName : nullptr;
FilterLen -= 2;
}
else
{
pFilteredStr = str_utf8_find_nocase(pName, aFilterStr);
}
if(pFilteredStr)
{
PrintFn(pFilteredStr, FilterLen);
return true;
}
}
return false;
}
void CMenus::FriendlistOnUpdate()
{
m_vFriends.clear();