Implement exact matches in search and exclude strings

Thanks to bencie for discussion
This commit is contained in:
Dennis Felsing 2022-12-23 23:23:44 +01:00
parent 50d0ecfda8
commit 68625dd370
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))
{
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
break;
@ -330,7 +347,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;
}
@ -350,23 +367,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();