mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Autocomplete chat commands (fixes #1504)
This commit is contained in:
parent
38333b65a1
commit
da08b7b86f
|
@ -32,9 +32,19 @@ CChat::CChat()
|
||||||
// reset the container indices, so the text containers can be deleted on reset
|
// reset the container indices, so the text containers can be deleted on reset
|
||||||
m_aLines[i].m_TextContainerIndex = -1;
|
m_aLines[i].m_TextContainerIndex = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CHAT_COMMAND(name, params, flags, callback, userdata, help) RegisterCommand(name, params, flags, help);
|
||||||
|
#include <game/server/ddracechat.h>
|
||||||
|
m_Commands.sort_range();
|
||||||
|
|
||||||
OnReset();
|
OnReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CChat::RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp)
|
||||||
|
{
|
||||||
|
m_Commands.add_unsorted(CCommand{pName, pParams});
|
||||||
|
}
|
||||||
|
|
||||||
void CChat::OnWindowResize()
|
void CChat::OnWindowResize()
|
||||||
{
|
{
|
||||||
for(int i = 0; i < MAX_LINES; i++)
|
for(int i = 0; i < MAX_LINES; i++)
|
||||||
|
@ -294,82 +304,150 @@ bool CChat::OnInput(IInput::CEvent Event)
|
||||||
str_copy(m_aCompletionBuffer, m_Input.GetString()+m_PlaceholderOffset, min(static_cast<int>(sizeof(m_aCompletionBuffer)), m_PlaceholderLength+1));
|
str_copy(m_aCompletionBuffer, m_Input.GetString()+m_PlaceholderOffset, min(static_cast<int>(sizeof(m_aCompletionBuffer)), m_PlaceholderLength+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// find next possible name
|
if(m_aCompletionBuffer[0] == '/')
|
||||||
const char *pCompletionString = 0;
|
{
|
||||||
|
CCommand *pCompletionCommand = 0;
|
||||||
|
|
||||||
|
const size_t NumCommands = m_Commands.size();
|
||||||
|
|
||||||
|
if(m_ReverseTAB)
|
||||||
|
m_CompletionChosen = (m_CompletionChosen-1 + 2*NumCommands)%(2*NumCommands);
|
||||||
|
else
|
||||||
|
m_CompletionChosen = (m_CompletionChosen+1)%(2*NumCommands);
|
||||||
|
|
||||||
|
const char *pCommandStart = m_aCompletionBuffer+1;
|
||||||
|
for(size_t i = 0; i < 2*NumCommands; ++i)
|
||||||
|
{
|
||||||
|
int SearchType;
|
||||||
|
int Index;
|
||||||
|
|
||||||
|
if(m_ReverseTAB)
|
||||||
|
{
|
||||||
|
SearchType = ((m_CompletionChosen-i +2*NumCommands)%(2*NumCommands))/NumCommands;
|
||||||
|
Index = (m_CompletionChosen-i + NumCommands )%NumCommands;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SearchType = ((m_CompletionChosen+i)%(2*NumCommands))/NumCommands;
|
||||||
|
Index = (m_CompletionChosen+i)%NumCommands;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &Command = m_Commands[Index];
|
||||||
|
|
||||||
|
if(str_comp_nocase_num(Command.pName, pCommandStart, str_length(pCommandStart)) == 0)
|
||||||
|
{
|
||||||
|
pCompletionCommand = &Command;
|
||||||
|
m_CompletionChosen = Index+SearchType*NumCommands;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the command
|
||||||
|
if(pCompletionCommand)
|
||||||
|
{
|
||||||
|
char aBuf[256];
|
||||||
|
// add part before the name
|
||||||
|
str_copy(aBuf, m_Input.GetString(), min(static_cast<int>(sizeof(aBuf)), m_PlaceholderOffset+1));
|
||||||
|
|
||||||
|
// add the command
|
||||||
|
str_append(aBuf, "/", sizeof(aBuf));
|
||||||
|
str_append(aBuf, pCompletionCommand->pName, sizeof(aBuf));
|
||||||
|
|
||||||
|
// add separator
|
||||||
|
const char *pSeparator = pCompletionCommand->pParams[0] == '\0' ? "" : " ";
|
||||||
|
str_append(aBuf, pSeparator, sizeof(aBuf));
|
||||||
|
if(*pSeparator)
|
||||||
|
str_append(aBuf, pSeparator, sizeof(aBuf));
|
||||||
|
|
||||||
|
// add part after the name
|
||||||
|
str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf));
|
||||||
|
|
||||||
|
m_PlaceholderLength = str_length(pSeparator)+str_length(pCompletionCommand->pName)+1;
|
||||||
|
m_OldChatStringLength = m_Input.GetLength();
|
||||||
|
m_Input.Set(aBuf); // TODO: Use Add instead
|
||||||
|
m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength);
|
||||||
|
m_InputUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// find next possible name
|
||||||
|
const char *pCompletionString = 0;
|
||||||
|
|
||||||
if(m_ReverseTAB)
|
if(m_ReverseTAB)
|
||||||
m_CompletionChosen = (m_CompletionChosen-1 + 2*MAX_CLIENTS)%(2*MAX_CLIENTS);
|
m_CompletionChosen = (m_CompletionChosen-1 + 2*MAX_CLIENTS)%(2*MAX_CLIENTS);
|
||||||
else
|
else
|
||||||
m_CompletionChosen = (m_CompletionChosen+1)%(2*MAX_CLIENTS);
|
m_CompletionChosen = (m_CompletionChosen+1)%(2*MAX_CLIENTS);
|
||||||
|
|
||||||
for(int i = 0; i < 2*MAX_CLIENTS; ++i)
|
for(int i = 0; i < 2*MAX_CLIENTS; ++i)
|
||||||
{
|
|
||||||
int SearchType;
|
|
||||||
int Index;
|
|
||||||
|
|
||||||
if(m_ReverseTAB)
|
|
||||||
{
|
{
|
||||||
SearchType = ((m_CompletionChosen-i +2*MAX_CLIENTS)%(2*MAX_CLIENTS))/MAX_CLIENTS;
|
int SearchType;
|
||||||
Index = (m_CompletionChosen-i + MAX_CLIENTS )%MAX_CLIENTS;
|
int Index;
|
||||||
}
|
|
||||||
else
|
if(m_ReverseTAB)
|
||||||
{
|
{
|
||||||
SearchType = ((m_CompletionChosen+i)%(2*MAX_CLIENTS))/MAX_CLIENTS;
|
SearchType = ((m_CompletionChosen-i +2*MAX_CLIENTS)%(2*MAX_CLIENTS))/MAX_CLIENTS;
|
||||||
Index = (m_CompletionChosen+i)%MAX_CLIENTS;
|
Index = (m_CompletionChosen-i + MAX_CLIENTS )%MAX_CLIENTS;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SearchType = ((m_CompletionChosen+i)%(2*MAX_CLIENTS))/MAX_CLIENTS;
|
||||||
|
Index = (m_CompletionChosen+i)%MAX_CLIENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if(!m_pClient->m_Snap.m_paInfoByName[Index])
|
if(!m_pClient->m_Snap.m_paInfoByName[Index])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int Index2 = m_pClient->m_Snap.m_paInfoByName[Index]->m_ClientID;
|
int Index2 = m_pClient->m_Snap.m_paInfoByName[Index]->m_ClientID;
|
||||||
|
|
||||||
bool Found = false;
|
bool Found = false;
|
||||||
if(SearchType == 1)
|
if(SearchType == 1)
|
||||||
{
|
{
|
||||||
if(str_utf8_comp_nocase_num(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)) &&
|
if(str_utf8_comp_nocase_num(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)) &&
|
||||||
str_utf8_find_nocase(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer))
|
str_utf8_find_nocase(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer))
|
||||||
|
Found = true;
|
||||||
|
}
|
||||||
|
else if(!str_utf8_comp_nocase_num(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)))
|
||||||
Found = true;
|
Found = true;
|
||||||
}
|
|
||||||
else if(!str_utf8_comp_nocase_num(m_pClient->m_aClients[Index2].m_aName, m_aCompletionBuffer, str_length(m_aCompletionBuffer)))
|
|
||||||
Found = true;
|
|
||||||
|
|
||||||
if(Found)
|
if(Found)
|
||||||
|
{
|
||||||
|
pCompletionString = m_pClient->m_aClients[Index2].m_aName;
|
||||||
|
m_CompletionChosen = Index+SearchType*MAX_CLIENTS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the name
|
||||||
|
if(pCompletionString)
|
||||||
{
|
{
|
||||||
pCompletionString = m_pClient->m_aClients[Index2].m_aName;
|
char aBuf[256];
|
||||||
m_CompletionChosen = Index+SearchType*MAX_CLIENTS;
|
// add part before the name
|
||||||
break;
|
str_copy(aBuf, m_Input.GetString(), min(static_cast<int>(sizeof(aBuf)), m_PlaceholderOffset+1));
|
||||||
|
|
||||||
|
// add the name
|
||||||
|
str_append(aBuf, pCompletionString, sizeof(aBuf));
|
||||||
|
|
||||||
|
// add separator
|
||||||
|
const char *pSeparator = "";
|
||||||
|
if(*(m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength) != ' ')
|
||||||
|
pSeparator = m_PlaceholderOffset == 0 ? ": " : " ";
|
||||||
|
else if(m_PlaceholderOffset == 0)
|
||||||
|
pSeparator = ":";
|
||||||
|
if(*pSeparator)
|
||||||
|
str_append(aBuf, pSeparator, sizeof(aBuf));
|
||||||
|
|
||||||
|
// add part after the name
|
||||||
|
str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf));
|
||||||
|
|
||||||
|
m_PlaceholderLength = str_length(pSeparator)+str_length(pCompletionString);
|
||||||
|
m_OldChatStringLength = m_Input.GetLength();
|
||||||
|
m_Input.Set(aBuf); // TODO: Use Add instead
|
||||||
|
m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength);
|
||||||
|
m_InputUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the name
|
|
||||||
if(pCompletionString)
|
|
||||||
{
|
|
||||||
char aBuf[256];
|
|
||||||
// add part before the name
|
|
||||||
str_copy(aBuf, m_Input.GetString(), min(static_cast<int>(sizeof(aBuf)), m_PlaceholderOffset+1));
|
|
||||||
|
|
||||||
// add the name
|
|
||||||
str_append(aBuf, pCompletionString, sizeof(aBuf));
|
|
||||||
|
|
||||||
// add separator
|
|
||||||
const char *pSeparator = "";
|
|
||||||
if(*(m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength) != ' ')
|
|
||||||
pSeparator = m_PlaceholderOffset == 0 ? ": " : " ";
|
|
||||||
else if(m_PlaceholderOffset == 0)
|
|
||||||
pSeparator = ":";
|
|
||||||
if(*pSeparator)
|
|
||||||
str_append(aBuf, pSeparator, sizeof(aBuf));
|
|
||||||
|
|
||||||
// add part after the name
|
|
||||||
str_append(aBuf, m_Input.GetString()+m_PlaceholderOffset+m_PlaceholderLength, sizeof(aBuf));
|
|
||||||
|
|
||||||
m_PlaceholderLength = str_length(pSeparator)+str_length(pCompletionString);
|
|
||||||
m_OldChatStringLength = m_Input.GetLength();
|
|
||||||
m_Input.Set(aBuf); // TODO: Use Add instead
|
|
||||||
m_Input.SetCursorOffset(m_PlaceholderOffset+m_PlaceholderLength);
|
|
||||||
m_InputUpdate = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,6 +60,17 @@ class CChat : public CComponent
|
||||||
int m_PlaceholderOffset;
|
int m_PlaceholderOffset;
|
||||||
int m_PlaceholderLength;
|
int m_PlaceholderLength;
|
||||||
|
|
||||||
|
struct CCommand
|
||||||
|
{
|
||||||
|
const char *pName;
|
||||||
|
const char *pParams;
|
||||||
|
|
||||||
|
bool operator <(const CCommand &Other) const { return str_comp(pName, Other.pName) < 0; }
|
||||||
|
bool operator <=(const CCommand &Other) const { return str_comp(pName, Other.pName) <= 0; }
|
||||||
|
bool operator ==(const CCommand &Other) const { return str_comp(pName, Other.pName) == 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
sorted_array<CCommand> m_Commands;
|
||||||
bool m_ReverseTAB;
|
bool m_ReverseTAB;
|
||||||
|
|
||||||
struct CHistoryEntry
|
struct CHistoryEntry
|
||||||
|
@ -85,14 +96,11 @@ public:
|
||||||
CChat();
|
CChat();
|
||||||
|
|
||||||
bool IsActive() const { return m_Mode != MODE_NONE; }
|
bool IsActive() const { return m_Mode != MODE_NONE; }
|
||||||
|
|
||||||
void AddLine(int ClientID, int Team, const char *pLine);
|
void AddLine(int ClientID, int Team, const char *pLine);
|
||||||
|
|
||||||
void EnableMode(int Team);
|
void EnableMode(int Team);
|
||||||
|
|
||||||
void Say(int Team, const char *pLine);
|
void Say(int Team, const char *pLine);
|
||||||
|
|
||||||
void SayChat(const char *pLine);
|
void SayChat(const char *pLine);
|
||||||
|
void RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp);
|
||||||
|
|
||||||
virtual void OnWindowResize();
|
virtual void OnWindowResize();
|
||||||
virtual void OnReset();
|
virtual void OnReset();
|
||||||
|
|
Loading…
Reference in a new issue