Improve auto-completition of nick names and add it to the in-game chat. Patch by trompetin17. Refs #1767.
This was SVN commit r16261.
This commit is contained in:
parent
3ee30f125c
commit
53b335f5ae
@ -240,3 +240,49 @@ function sanitizePlayerName(name, stripUnicode, stripSpaces)
|
||||
// Limit the length to 20 characters
|
||||
return sanitizedName.substr(0,20);
|
||||
}
|
||||
|
||||
function tryAutoComplete(text, autoCompleteList)
|
||||
{
|
||||
if (!text.length)
|
||||
return text;
|
||||
|
||||
var wordSplit = text.split(/\s/g);
|
||||
if (!wordSplit.length)
|
||||
return text;
|
||||
|
||||
var lastWord = wordSplit.pop();
|
||||
if (!lastWord.length)
|
||||
return text;
|
||||
|
||||
for (var word of autoCompleteList)
|
||||
{
|
||||
if (word.toLowerCase().indexOf(lastWord.toLowerCase()) != 0)
|
||||
continue;
|
||||
|
||||
text = wordSplit.join(" ")
|
||||
if (text.length > 0)
|
||||
text += " ";
|
||||
|
||||
text += word;
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function autoCompleteNick(guiName, playerList)
|
||||
{
|
||||
var input = Engine.GetGUIObjectByName(guiName);
|
||||
var text = input.caption;
|
||||
if (!text.length)
|
||||
return;
|
||||
|
||||
var autoCompleteList = [];
|
||||
for (var player of playerList)
|
||||
autoCompleteList.push(player.name);
|
||||
|
||||
var bufferPosition = input.buffer_position;
|
||||
var textTillBufferPosition = text.substring(0, bufferPosition);
|
||||
var newText = tryAutoComplete(textTillBufferPosition, autoCompleteList);
|
||||
input.caption = newText + text.substring(bufferPosition);
|
||||
input.buffer_position = bufferPosition + (newText.length - textTillBufferPosition.length);
|
||||
}
|
||||
|
@ -752,35 +752,6 @@ function submitChatInput()
|
||||
}
|
||||
}
|
||||
|
||||
function completeNick()
|
||||
{
|
||||
var input = Engine.GetGUIObjectByName("chatInput");
|
||||
var text = escapeText(input.caption);
|
||||
if (text.length)
|
||||
{
|
||||
var matched = false;
|
||||
for each (var playerObj in Engine.GetPlayerList())
|
||||
{
|
||||
var player = playerObj.name;
|
||||
var breaks = text.match(/(\s+)/g) || [];
|
||||
text.split(/\s+/g).reduceRight(function (wordsSoFar, word, index)
|
||||
{
|
||||
if (matched)
|
||||
return null;
|
||||
var matchCandidate = word + (breaks[index - 1] || "") + wordsSoFar;
|
||||
if (player.toUpperCase().indexOf(matchCandidate.toUpperCase().trim()) == 0)
|
||||
{
|
||||
input.caption = text.replace(matchCandidate.trim(), player);
|
||||
matched = true;
|
||||
}
|
||||
return matchCandidate;
|
||||
}, "");
|
||||
if (matched)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isValidNick(nick)
|
||||
{
|
||||
var prohibitedNicks = ["system"];
|
||||
|
@ -231,7 +231,7 @@
|
||||
<object name="chatText" size="0 0 100% 94%" type="text" style="ChatPanel" font="sans-13"/>
|
||||
<object name="chatInput" size="0 94% 100% 100%" type="input" style="ModernInput" font="sans-13">
|
||||
<action on="Press">submitChatInput();</action>
|
||||
<action on="Tab">completeNick();</action>
|
||||
<action on="Tab">autoCompleteNick("chatInput", Engine.GetPlayerList());</action>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
|
@ -197,6 +197,7 @@
|
||||
<object name="chatDialogPanel" size="50%-180 50%-48 50%+180 50%+36" type="image" hidden="true" sprite="genericPanel">
|
||||
<object name="chatInput" size="16 12 100%-16 36" type="input" style="ModernInput" max_length="80">
|
||||
<action on="Press">submitChatInput();</action>
|
||||
<action on="Tab">autoCompleteNick("chatInput", g_Players);</action>
|
||||
</object>
|
||||
|
||||
<object size="16 100%-40 30%+16 100%-12" type="button" style="StoneButton">
|
||||
|
@ -50,6 +50,7 @@ CInput::CInput()
|
||||
m_PrevTime(0.0), m_CursorVisState(true), m_CursorBlinkRate(0.5), m_ComposingText(false),
|
||||
m_iComposedLength(0), m_iComposedPos(0), m_iInsertPos(0)
|
||||
{
|
||||
AddSetting(GUIST_int, "buffer_position");
|
||||
AddSetting(GUIST_float, "buffer_zone");
|
||||
AddSetting(GUIST_CStrW, "caption");
|
||||
AddSetting(GUIST_int, "cell_id");
|
||||
@ -79,11 +80,18 @@ CInput::~CInput()
|
||||
{
|
||||
}
|
||||
|
||||
void CInput::UpdateBufferPositionSetting()
|
||||
{
|
||||
int* bufferPos = (int*)m_Settings["buffer_position"].m_pSetting;
|
||||
*bufferPos = m_iBufferPos;
|
||||
}
|
||||
|
||||
void CInput::ClearComposedText()
|
||||
{
|
||||
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
|
||||
pCaption->erase(m_iInsertPos, m_iComposedLength);
|
||||
m_iBufferPos = m_iInsertPos;
|
||||
UpdateBufferPositionSetting();
|
||||
m_iComposedLength = 0;
|
||||
m_iComposedPos = 0;
|
||||
}
|
||||
@ -126,6 +134,7 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
|
||||
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
|
||||
|
||||
m_iBufferPos += text.length();
|
||||
UpdateBufferPositionSetting();
|
||||
m_iBufferPos_Tail = -1;
|
||||
|
||||
UpdateAutoScroll();
|
||||
@ -171,6 +180,7 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
|
||||
m_iBufferPos_Tail = -1;
|
||||
}
|
||||
|
||||
UpdateBufferPositionSetting();
|
||||
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
|
||||
|
||||
UpdateAutoScroll();
|
||||
@ -551,6 +561,7 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateBufferPositionSetting();
|
||||
return IN_HANDLED;
|
||||
}
|
||||
|
||||
@ -585,6 +596,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
||||
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
|
||||
|
||||
m_iBufferPos += (int)wcslen(text);
|
||||
UpdateBufferPositionSetting();
|
||||
|
||||
sys_clipboard_free(text);
|
||||
}
|
||||
@ -660,6 +672,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
||||
}
|
||||
}
|
||||
|
||||
UpdateBufferPositionSetting();
|
||||
DeleteCurSelection();
|
||||
}
|
||||
return IN_HANDLED;
|
||||
@ -692,6 +705,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
||||
|
||||
m_iBufferPos++;
|
||||
}
|
||||
UpdateBufferPositionSetting();
|
||||
DeleteCurSelection();
|
||||
}
|
||||
return IN_HANDLED;
|
||||
@ -748,6 +762,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
||||
m_iBufferPos_Tail = -1;
|
||||
}
|
||||
|
||||
UpdateBufferPositionSetting();
|
||||
UpdateAutoScroll();
|
||||
|
||||
return IN_HANDLED;
|
||||
@ -796,6 +811,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
|
||||
m_iBufferPos_Tail = -1;
|
||||
}
|
||||
|
||||
UpdateBufferPositionSetting();
|
||||
UpdateAutoScroll();
|
||||
|
||||
return IN_HANDLED;
|
||||
@ -841,6 +857,12 @@ void CInput::HandleMessage(SGUIMessage &Message)
|
||||
GetScrollBar(0).SetScrollBarStyle(scrollbar_style);
|
||||
}
|
||||
|
||||
if (Message.value == CStr("buffer_position"))
|
||||
{
|
||||
GUI<int>::GetSetting(this, Message.value, m_iBufferPos);
|
||||
m_iBufferPos_Tail = -1; // position change resets selection
|
||||
}
|
||||
|
||||
if (Message.value == CStr("size") ||
|
||||
Message.value == CStr("z") ||
|
||||
Message.value == CStr("font") ||
|
||||
@ -857,14 +879,10 @@ void CInput::HandleMessage(SGUIMessage &Message)
|
||||
bool multiline;
|
||||
GUI<bool>::GetSetting(this, "multiline", multiline);
|
||||
|
||||
if (multiline == false)
|
||||
{
|
||||
if (!multiline)
|
||||
GetScrollBar(0).SetLength(0.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetScrollBar(0).SetLength(m_CachedActualSize.bottom - m_CachedActualSize.top);
|
||||
}
|
||||
|
||||
UpdateText();
|
||||
}
|
||||
@ -893,13 +911,9 @@ void CInput::HandleMessage(SGUIMessage &Message)
|
||||
// should of course be placed accordingly. Other
|
||||
// special cases are handled like the input box norms.
|
||||
if (g_keys[SDLK_RSHIFT] || g_keys[SDLK_LSHIFT])
|
||||
{
|
||||
m_iBufferPos = GetMouseHoveringTextPosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iBufferPos = m_iBufferPos_Tail = GetMouseHoveringTextPosition();
|
||||
}
|
||||
|
||||
m_SelectingText = true;
|
||||
|
||||
@ -1098,6 +1112,7 @@ void CInput::HandleMessage(SGUIMessage &Message)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UpdateBufferPositionSetting();
|
||||
}
|
||||
|
||||
void CInput::UpdateCachedSize()
|
||||
@ -1552,6 +1567,7 @@ void CInput::UpdateText(int from, int to_before, int to_after)
|
||||
// Ensure positions are valid after caption changes
|
||||
m_iBufferPos = std::min(m_iBufferPos, (int)caption.size());
|
||||
m_iBufferPos_Tail = std::min(m_iBufferPos_Tail, (int)caption.size());
|
||||
UpdateBufferPositionSetting();
|
||||
|
||||
if (font_name.empty())
|
||||
{
|
||||
@ -2053,6 +2069,7 @@ void CInput::DeleteCurSelection()
|
||||
// Remove selection
|
||||
m_iBufferPos_Tail = -1;
|
||||
m_iBufferPos = virtualFrom;
|
||||
UpdateBufferPositionSetting();
|
||||
}
|
||||
|
||||
bool CInput::SelectingText() const
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
/* Copyright (C) 2015 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -36,9 +36,6 @@ GUI Object - Input [box]
|
||||
//--------------------------------------------------------
|
||||
#include "GUI.h"
|
||||
|
||||
// TODO Gee: Remove
|
||||
class IGUIScrollBar;
|
||||
|
||||
//--------------------------------------------------------
|
||||
// Macros
|
||||
//--------------------------------------------------------
|
||||
@ -111,53 +108,54 @@ protected:
|
||||
*/
|
||||
virtual void Draw();
|
||||
|
||||
// Calculate m_CharacterPosition
|
||||
// the main task for this function is to perfom word-wrapping
|
||||
// You input from which character it has been changed, because
|
||||
// if we add a character to the very last end, we don't want
|
||||
// process everything all over again! Also notice you can
|
||||
// specify a 'to' also, it will only be used though if a '\n'
|
||||
// appears, because then the word-wrapping won't change after
|
||||
// that.
|
||||
/**
|
||||
* Calculate m_CharacterPosition
|
||||
* the main task for this function is to perfom word-wrapping
|
||||
* You input from which character it has been changed, because
|
||||
* if we add a character to the very last end, we don't want
|
||||
* process everything all over again! Also notice you can
|
||||
* specify a 'to' also, it will only be used though if a '\n'
|
||||
* appears, because then the word-wrapping won't change after
|
||||
* that.
|
||||
*/
|
||||
void UpdateText(int from=0, int to_before=-1, int to_after=-1);
|
||||
|
||||
// Delete the current selection. Also places the pointer at the
|
||||
// crack between the two segments kept.
|
||||
/**
|
||||
* Delete the current selection. Also places the pointer at the
|
||||
* crack between the two segments kept.
|
||||
*/
|
||||
void DeleteCurSelection();
|
||||
|
||||
// Is text selected? It can be denote two ways, m_iBufferPos_Tail
|
||||
// being -1 or the same as m_iBufferPos. This makes for clearer
|
||||
// code.
|
||||
/**
|
||||
* Is text selected? It can be denote two ways, m_iBufferPos_Tail
|
||||
* being -1 or the same as m_iBufferPos. This makes for clearer
|
||||
* code.
|
||||
*/
|
||||
bool SelectingText() const;
|
||||
|
||||
// Get area of where text can be drawn.
|
||||
/// Get area of where text can be drawn.
|
||||
float GetTextAreaWidth();
|
||||
|
||||
// Called every time the auto-scrolling should be checked.
|
||||
/// Called every time the auto-scrolling should be checked.
|
||||
void UpdateAutoScroll();
|
||||
|
||||
// Clear composed IME input when supported (SDL2 only).
|
||||
/// Clear composed IME input when supported (SDL2 only).
|
||||
void ClearComposedText();
|
||||
|
||||
/// Updates the buffer (cursor) position exposed to JS.
|
||||
void UpdateBufferPositionSetting();
|
||||
protected:
|
||||
// Cursor position
|
||||
// (the second one is for selection of larger areas, -1 if not used)
|
||||
// A note on 'Tail', it was first called 'To', and the natural order
|
||||
// of X and X_To was X then X_To. Now Tail is called so, because it
|
||||
// can be placed both before and after, but the important things is
|
||||
// that m_iBufferPos is ALWAYS where the edit pointer is. Yes, there
|
||||
// is an edit pointer even though you select a larger area. For instance
|
||||
// if you want to resize the selection with Shift+Left/Right, there
|
||||
// are always two ways a selection can look. Check any OS GUI and you'll
|
||||
// see.
|
||||
int m_iBufferPos,
|
||||
m_iBufferPos_Tail;
|
||||
/// Cursor position
|
||||
int m_iBufferPos;
|
||||
/// Cursor position we started to select from. (-1 if not selecting)
|
||||
/// (NB: Can be larger than m_iBufferPos if selecting from back to front.)
|
||||
int m_iBufferPos_Tail;
|
||||
|
||||
// If we're composing text with an IME
|
||||
/// If we're composing text with an IME
|
||||
bool m_ComposingText;
|
||||
// The length and position of the current IME composition
|
||||
/// The length and position of the current IME composition
|
||||
int m_iComposedLength, m_iComposedPos;
|
||||
// The position to insert committed text
|
||||
/// The position to insert committed text
|
||||
int m_iInsertPos;
|
||||
|
||||
// the outer vector is lines, and the inner is X positions
|
||||
@ -166,40 +164,44 @@ protected:
|
||||
// pointer should be placed when the input control is pressed.
|
||||
struct SRow
|
||||
{
|
||||
int m_ListStart; // Where does the Row starts
|
||||
std::vector<float> m_ListOfX; // List of X values for each character.
|
||||
int m_ListStart; /// Where does the Row starts
|
||||
std::vector<float> m_ListOfX; /// List of X values for each character.
|
||||
};
|
||||
|
||||
// List of rows, list because I use a lot of iterators, and change
|
||||
// its size continuously, it's easier and safer if I know the
|
||||
// iterators never gets invalidated.
|
||||
// For one-liners, only one row is used.
|
||||
/**
|
||||
* List of rows to ease changing its size, so iterators stay valid.
|
||||
* For one-liners only one row is used.
|
||||
*/
|
||||
std::list< SRow > m_CharacterPositions;
|
||||
|
||||
// *** Things for a multi-lined input control *** //
|
||||
|
||||
// This is when you change row with up/down, and the row you jump
|
||||
// to doesn't have anything at that X position, then it will
|
||||
// keep the WantedX position in mind when switching to the next
|
||||
// row. It will keep on being used until it reach a row which meets
|
||||
// the requirements.
|
||||
// 0.0f means not in use.
|
||||
/**
|
||||
* When you change row with up/down, and the row you jump to does
|
||||
* not have anything at that X position, then it will keep the
|
||||
* m_WantedX position in mind when switching to the next row.
|
||||
* It will keep on being used until it reach a row which meets the
|
||||
* requirements.
|
||||
* 0.0f means not in use.
|
||||
*/
|
||||
float m_WantedX;
|
||||
|
||||
// If we are in the process of selecting a larger selection of text
|
||||
// using the mouse click (hold) and drag, this is true.
|
||||
/**
|
||||
* If we are in the process of selecting a larger selection of text
|
||||
* using the mouse click (hold) and drag, this is true.
|
||||
*/
|
||||
bool m_SelectingText;
|
||||
|
||||
// *** Things for one-line input control *** //
|
||||
float m_HorizontalScroll;
|
||||
|
||||
// Used to store the previous time for flashing the cursor.
|
||||
/// Used to store the previous time for flashing the cursor.
|
||||
double m_PrevTime;
|
||||
|
||||
// Cursor blink rate in seconds, if greater than 0.0.
|
||||
/// Cursor blink rate in seconds, if greater than 0.0.
|
||||
double m_CursorBlinkRate;
|
||||
|
||||
// If the cursor should be drawn or not.
|
||||
/// If the cursor should be drawn or not.
|
||||
bool m_CursorVisState;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user