emotes: change autocompletion behavior to work like console
This commit is contained in:
parent
3bc79a8f93
commit
4fbe2c31fe
3 changed files with 121 additions and 75 deletions
|
|
@ -96,8 +96,7 @@ static boolean headsupactive = false;
|
|||
boolean hu_showscores; // draw rankings
|
||||
static char hu_tick;
|
||||
static tic_t hu_emoteanim = 0;
|
||||
#define MAXEMOTESUGGESTIONS 8
|
||||
static emote_t *emote_suggestions[MAXEMOTESUGGESTIONS] = {0};
|
||||
emote_autocomplete_t emote_autocomplete = {0};
|
||||
|
||||
//-------------------------------------------
|
||||
// misc vars
|
||||
|
|
@ -1194,6 +1193,7 @@ boolean HU_Responder(event_t *ev)
|
|||
chat_on = true;
|
||||
w_chat_buf[0] = 0;
|
||||
M_TextInputInit(&w_chat, w_chat_buf, sizeof w_chat_buf);
|
||||
memset(&emote_autocomplete, 0, sizeof(emote_autocomplete));
|
||||
teamtalk = false;
|
||||
chat_scrollmedown = true;
|
||||
typelines = 1;
|
||||
|
|
@ -1205,6 +1205,7 @@ boolean HU_Responder(event_t *ev)
|
|||
chat_on = true;
|
||||
w_chat_buf[0] = 0;
|
||||
M_TextInputInit(&w_chat, w_chat_buf, sizeof w_chat_buf);
|
||||
memset(&emote_autocomplete, 0, sizeof(emote_autocomplete));
|
||||
teamtalk = G_GametypeHasTeams(); // Don't teamtalk if we don't have teams.
|
||||
chat_scrollmedown = true;
|
||||
typelines = 1;
|
||||
|
|
@ -1228,7 +1229,7 @@ boolean HU_Responder(event_t *ev)
|
|||
&& !G_ControlBoundToKey(0, gc_talkkey, ev->data1, false))
|
||||
return false;
|
||||
|
||||
M_TextInputHandleEmotes(&w_chat, c, emote_suggestions, MAXEMOTESUGGESTIONS);
|
||||
M_TextInputHandleEmotes(&w_chat, c, &emote_autocomplete);
|
||||
|
||||
if (c == KEY_ENTER)
|
||||
{
|
||||
|
|
@ -1775,17 +1776,37 @@ static void HU_DrawChat(void)
|
|||
}
|
||||
}
|
||||
|
||||
if (emote_suggestions[0])
|
||||
if (emote_autocomplete.emotestart != -1 && (emote_autocomplete.complete[0] || (w_chat.cursor - emote_autocomplete.emotestart) > 0))
|
||||
{
|
||||
emote_t *suggest;
|
||||
int skip = 0;
|
||||
|
||||
const char *complete;
|
||||
int complete_len;
|
||||
|
||||
if (emote_autocomplete.complete[0])
|
||||
{
|
||||
complete = emote_autocomplete.complete;
|
||||
complete_len = strlen(complete);
|
||||
}
|
||||
else
|
||||
{
|
||||
complete = &w_chat_buf[emote_autocomplete.emotestart];
|
||||
complete_len = w_chat.cursor - emote_autocomplete.emotestart;
|
||||
}
|
||||
|
||||
// A bit of copy-paste from /pm code :p
|
||||
INT32 suggesty = chaty - charheight - 1;
|
||||
size_t longest_suggestion_length = 0;
|
||||
|
||||
for (i = 0; i < MAXEMOTESUGGESTIONS && emote_suggestions[i]; ++i)
|
||||
while ((suggest = M_FindEmote(complete, complete_len, skip)) != NULL)
|
||||
{
|
||||
longest_suggestion_length = max(longest_suggestion_length, strlen(emote_suggestions[i]->name));
|
||||
longest_suggestion_length = max(longest_suggestion_length, strlen(suggest->name));
|
||||
++skip;
|
||||
}
|
||||
|
||||
skip = 0;
|
||||
|
||||
#ifdef NETSPLITSCREEN
|
||||
if (splitscreen)
|
||||
{
|
||||
|
|
@ -1797,11 +1818,19 @@ static void HU_DrawChat(void)
|
|||
#endif
|
||||
suggesty -= (cv_kartspeedometer.value ? 16 : 0);
|
||||
|
||||
for (i = 0; i < MAXEMOTESUGGESTIONS && emote_suggestions[i]; ++i)
|
||||
while ((suggest = M_FindEmote(complete, complete_len, skip)) != NULL)
|
||||
{
|
||||
V_DrawFillConsoleMap(chatx + boxw + 2, suggesty - (7*i), (longest_suggestion_length+2)*4 + EMOTEWIDTH, 6, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
V_DrawSmallString(chatx + boxw + 4 + EMOTEWIDTH, suggesty - (7*i), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va(":%s:", emote_suggestions[i]->name));
|
||||
HU_DrawEmote(chatx + boxw + 2, suggesty - i*7, emote_suggestions[i], V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
V_DrawFillConsoleMap(chatx + boxw + 2, suggesty - (7*skip), (longest_suggestion_length+2)*4 + EMOTEWIDTH, 6, V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
|
||||
// Highlight currently suggested emote
|
||||
int hlflag = 0;
|
||||
if (skip == emote_autocomplete.skip && emote_autocomplete.complete[0])
|
||||
hlflag = V_YELLOWMAP;
|
||||
|
||||
V_DrawSmallString(chatx + boxw + 4 + EMOTEWIDTH, suggesty - (7*skip), hlflag|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va(":%s:", suggest->name));
|
||||
HU_DrawEmote(chatx + boxw + 2, suggesty - skip*7, suggest, V_SNAPTOBOTTOM|V_SNAPTOLEFT);
|
||||
|
||||
++skip;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,21 +401,13 @@ boolean M_TextInputHandle(textinput_t *input, INT32 key)
|
|||
return M_TextInputHandleBase(input, key, false);
|
||||
}
|
||||
|
||||
boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_t *suggestions[], int maxsuggestions)
|
||||
static int M_TextInputEmoteStart(textinput_t *input)
|
||||
{
|
||||
boolean ret = M_TextInputHandleBase(input, key, true);
|
||||
if (input->cursor == 0)
|
||||
return -1;
|
||||
|
||||
// After this handled key we ended up inside an emote, lets fix that
|
||||
if (M_TextInputCheckEmote(input))
|
||||
M_TextInputToWordEnd(input, !shiftdown);
|
||||
int emotestart = -1;
|
||||
|
||||
// Always clear the first entry
|
||||
suggestions[0] = NULL;
|
||||
|
||||
// For autocomplete
|
||||
int emotestart = 0;
|
||||
|
||||
// Try find suggestions for emote names
|
||||
for (int i = input->cursor-1; i >= 0 && input->cursor-i <= MAXEMOTENAME; --i)
|
||||
{
|
||||
// Space can't be part of emote name
|
||||
|
|
@ -426,71 +418,90 @@ boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_t *suggesti
|
|||
if (input->buffer[i] == ':')
|
||||
{
|
||||
emotestart = i+1;
|
||||
// ...But only if we typed at least something
|
||||
if ((int)input->cursor-(i-1) < 3)
|
||||
break;
|
||||
|
||||
for (int skip = 0; skip < maxsuggestions; ++skip)
|
||||
{
|
||||
suggestions[skip] = M_FindEmote(input->buffer+i+1, input->cursor-i-1, skip);
|
||||
|
||||
// No more suggestions
|
||||
if (!suggestions[skip])
|
||||
break;
|
||||
}
|
||||
|
||||
// In any case, we found what we wanted, can exit the loop now
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const boolean single_suggestion = suggestions[0] && (maxsuggestions == 1 || !suggestions[1]);
|
||||
return emotestart;
|
||||
}
|
||||
|
||||
// If we suggest emotes, try autocomplete
|
||||
if ((key == '\t' && suggestions[0]) || (key == KEY_ENTER && single_suggestion))
|
||||
static void M_TextInputCompleteEmote(textinput_t *input, emote_autocomplete_t *autocomplete)
|
||||
{
|
||||
if (input->cursor == 0)
|
||||
return;
|
||||
|
||||
// If we're in "autocomplete" state, delete ':' for latest autocompleted emote
|
||||
if (autocomplete->complete[0])
|
||||
{
|
||||
int pos = 0;
|
||||
const int insertpos = (input->cursor-emotestart); // Only insert after that index (including it)
|
||||
boolean autocomplete = true;
|
||||
|
||||
while (autocomplete)
|
||||
if (input->buffer[input->cursor-1] == ':')
|
||||
{
|
||||
// Check if current character matches for all suggestions
|
||||
char c = suggestions[0]->name[pos];
|
||||
|
||||
// End of string reached
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
for (int i = 1; i < maxsuggestions && suggestions[i]; ++i)
|
||||
{
|
||||
if (suggestions[i]->name[pos] != c)
|
||||
{
|
||||
autocomplete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++pos;
|
||||
|
||||
if (autocomplete && pos > insertpos)
|
||||
M_TextInputAddChar(input, c);
|
||||
M_TextInputDelChar(input);
|
||||
}
|
||||
|
||||
// This was the only suggestion, finish autocomplete with a ':' and clear suggestions
|
||||
if (single_suggestion)
|
||||
autocomplete->skip++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to find emote to autocomplete
|
||||
|
||||
int emotestart = autocomplete->emotestart;
|
||||
|
||||
// No emote - no autocomplete
|
||||
if (emotestart == -1 || (size_t)emotestart == input->cursor)
|
||||
return;
|
||||
|
||||
strlcpy(autocomplete->complete, &input->buffer[emotestart], input->cursor-emotestart+1);
|
||||
}
|
||||
|
||||
emote_t *completed = M_FindEmote(autocomplete->complete, strlen(autocomplete->complete), autocomplete->skip);
|
||||
|
||||
// We hit end of list of suggested emotes
|
||||
if (completed == NULL)
|
||||
{
|
||||
autocomplete->skip = 0;
|
||||
completed = M_FindEmote(autocomplete->complete, strlen(autocomplete->complete), autocomplete->skip);
|
||||
|
||||
// NULL again, list was empty to begin with
|
||||
if (completed == NULL)
|
||||
{
|
||||
memcpy(&input->buffer[emotestart], suggestions[0]->name, strlen(suggestions[0]->name));
|
||||
M_TextInputAddChar(input, ':');
|
||||
suggestions[0] = NULL;
|
||||
}
|
||||
else if (pos > emotestart)
|
||||
{
|
||||
// Otherwise make sure beginning of emote matches
|
||||
memcpy(&input->buffer[emotestart], suggestions[0]->name, pos-emotestart);
|
||||
// Clear autocompletion state
|
||||
autocomplete->complete[0] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Erase previously typed emote. Not really efficent but too lazy to count chars and do M_TextInputDel()
|
||||
while (input->buffer[input->cursor-1] != ':')
|
||||
{
|
||||
M_TextInputDelChar(input);
|
||||
}
|
||||
|
||||
M_TextInputAddString(input, completed->name);
|
||||
M_TextInputAddChar(input, ':');
|
||||
}
|
||||
|
||||
boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_autocomplete_t *autocomplete)
|
||||
{
|
||||
boolean ret = M_TextInputHandleBase(input, key, true);
|
||||
|
||||
// After this handled key we ended up inside an emote, lets fix that
|
||||
if (M_TextInputCheckEmote(input))
|
||||
M_TextInputToWordEnd(input, !shiftdown);
|
||||
|
||||
// Always update it
|
||||
autocomplete->emotestart = M_TextInputEmoteStart(input);
|
||||
|
||||
if (key == '\t' || (autocomplete->complete[0] == 0 && key == KEY_ENTER))
|
||||
{
|
||||
M_TextInputCompleteEmote(input, autocomplete);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset state just in case
|
||||
autocomplete->complete[0] = 0;
|
||||
autocomplete->skip = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ typedef struct textinput_s {
|
|||
unsigned skull;
|
||||
} textinput_t;
|
||||
|
||||
typedef struct emote_autocomplete_s {
|
||||
char complete[MAXEMOTENAME+1];
|
||||
int skip;
|
||||
int emotestart;
|
||||
} emote_autocomplete_t;
|
||||
|
||||
void M_TextInputInit(textinput_t *input, char *buffer, size_t buffer_size);
|
||||
|
||||
void M_TextInputClear(textinput_t *input);
|
||||
|
|
@ -25,7 +31,7 @@ boolean M_TextInputHandle(textinput_t *input, INT32 key);
|
|||
|
||||
// Does everything same as M_TextInputHandle, but also handles emotes in input string
|
||||
// If currently typing something that looks like an existing emote, return suggestions for it
|
||||
boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_t *suggestions[], int maxsuggestions);
|
||||
boolean M_TextInputHandleEmotes(textinput_t *input, INT32 key, emote_autocomplete_t *autocomplete);
|
||||
|
||||
#define M_DrawTextInput(x, y, input, flags) M_DrawTextInputScroll((x), (y), (input), (flags), ((input)->buffer_size-1)*8)
|
||||
void M_DrawTextInputScroll(INT32 x, INT32 y, textinput_t *input, INT32 flags, INT32 MAXINPUTWIDTH);
|
||||
|
|
|
|||
Loading…
Reference in a new issue