mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge #6180
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:
commit
6d3af742dd
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue