ddnet/src/game/client/components/chat.cpp

1232 lines
37 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2008-08-27 19:41:02 +00:00
2019-04-05 23:15:02 +00:00
#include <engine/editor.h>
2010-05-29 07:25:38 +00:00
#include <engine/graphics.h>
#include <engine/keys.h>
#include <engine/shared/config.h>
2020-06-22 20:31:41 +00:00
#include <engine/shared/csv.h>
2020-09-22 16:02:03 +00:00
#include <engine/textrender.h>
2020-09-22 16:02:03 +00:00
#include <game/generated/protocol.h>
2020-10-13 20:08:52 +00:00
#include <game/client/animstate.h>
2010-05-29 07:25:38 +00:00
#include <game/client/gameclient.h>
2008-08-29 05:34:18 +00:00
#include <game/client/components/console.h>
#include <game/client/components/scoreboard.h>
2020-10-13 20:08:52 +00:00
#include <game/client/components/skins.h>
2010-05-29 07:25:38 +00:00
#include <game/client/components/sounds.h>
#include <game/localization.h>
2010-05-29 07:25:38 +00:00
#include "chat.h"
CChat::CChat()
{
2020-10-26 14:14:07 +00:00
for(auto &Line : m_aLines)
2018-03-13 20:50:49 +00:00
{
// reset the container indices, so the text containers can be deleted on reset
2020-10-26 14:14:07 +00:00
Line.m_TextContainerIndex = -1;
Line.m_QuadContainerIndex = -1;
2018-03-13 20:50:49 +00:00
}
2020-09-22 16:02:03 +00:00
#define CHAT_COMMAND(name, params, flags, callback, userdata, help) RegisterCommand(name, params, flags, help);
#include <game/ddracechat.h>
2022-06-29 21:46:28 +00:00
#undef CHAT_COMMAND
std::sort(m_vCommands.begin(), m_vCommands.end());
m_Mode = MODE_NONE;
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.SetClipboardLineCallback([this](const char *pStr) { SayChat(pStr); });
2010-05-29 07:25:38 +00:00
}
void CChat::RegisterCommand(const char *pName, const char *pParams, int flags, const char *pHelp)
{
m_vCommands.emplace_back(pName, pParams);
}
2020-10-13 20:08:52 +00:00
void CChat::RebuildChat()
{
2020-10-26 14:14:07 +00:00
for(auto &Line : m_aLines)
{
TextRender()->DeleteTextContainer(Line.m_TextContainerIndex);
Graphics()->DeleteQuadContainer(Line.m_QuadContainerIndex);
2020-09-28 16:30:27 +00:00
// recalculate sizes
Line.m_aYOffset[0] = -1.f;
Line.m_aYOffset[1] = -1.f;
}
}
2020-10-13 20:08:52 +00:00
void CChat::OnWindowResize()
{
RebuildChat();
}
void CChat::Reset()
2010-05-29 07:25:38 +00:00
{
2020-10-26 14:14:07 +00:00
for(auto &Line : m_aLines)
2009-05-31 09:44:20 +00:00
{
TextRender()->DeleteTextContainer(Line.m_TextContainerIndex);
Graphics()->DeleteQuadContainer(Line.m_QuadContainerIndex);
2020-10-26 14:14:07 +00:00
Line.m_Time = 0;
Line.m_aText[0] = 0;
Line.m_aName[0] = 0;
Line.m_Friend = false;
Line.m_TimesRepeated = 0;
Line.m_HasRenderTee = false;
2010-05-29 07:25:38 +00:00
}
m_PrevScoreBoardShowed = false;
2018-03-13 20:50:49 +00:00
m_PrevShowChat = false;
m_Show = false;
m_CompletionUsed = false;
m_CompletionChosen = -1;
m_aCompletionBuffer[0] = 0;
m_PlaceholderOffset = 0;
m_PlaceholderLength = 0;
2011-03-20 15:17:06 +00:00
m_pHistoryEntry = 0x0;
2012-01-09 22:13:51 +00:00
m_PendingChatCounter = 0;
m_LastChatSend = 0;
m_CurrentLine = 0;
DisableMode();
2021-06-23 05:05:49 +00:00
for(int64_t &LastSoundPlayed : m_aLastSoundPlayed)
2020-10-26 14:14:07 +00:00
LastSoundPlayed = 0;
2010-05-29 07:25:38 +00:00
}
void CChat::OnRelease()
{
m_Show = false;
}
2010-05-29 07:25:38 +00:00
void CChat::OnStateChange(int NewState, int OldState)
{
if(OldState <= IClient::STATE_CONNECTING)
Reset();
}
2010-05-29 07:25:38 +00:00
void CChat::ConSay(IConsole::IResult *pResult, void *pUserData)
2008-08-27 19:41:02 +00:00
{
2020-09-22 16:02:03 +00:00
((CChat *)pUserData)->Say(0, pResult->GetString(0));
2008-08-27 19:41:02 +00:00
}
2010-05-29 07:25:38 +00:00
void CChat::ConSayTeam(IConsole::IResult *pResult, void *pUserData)
2008-08-27 19:41:02 +00:00
{
2020-09-22 16:02:03 +00:00
((CChat *)pUserData)->Say(1, pResult->GetString(0));
2008-08-27 19:41:02 +00:00
}
2010-05-29 07:25:38 +00:00
void CChat::ConChat(IConsole::IResult *pResult, void *pUserData)
2008-08-27 19:41:02 +00:00
{
2010-05-29 07:25:38 +00:00
const char *pMode = pResult->GetString(0);
if(str_comp(pMode, "all") == 0)
2020-09-22 16:02:03 +00:00
((CChat *)pUserData)->EnableMode(0);
2010-05-29 07:25:38 +00:00
else if(str_comp(pMode, "team") == 0)
2020-09-22 16:02:03 +00:00
((CChat *)pUserData)->EnableMode(1);
2008-08-27 19:41:02 +00:00
else
2020-09-22 16:02:03 +00:00
((CChat *)pUserData)->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", "expected all or team as mode");
if(pResult->GetString(1)[0] || g_Config.m_ClChatReset)
2020-09-22 16:02:03 +00:00
((CChat *)pUserData)->m_Input.Set(pResult->GetString(1));
2008-08-27 19:41:02 +00:00
}
void CChat::ConShowChat(IConsole::IResult *pResult, void *pUserData)
{
((CChat *)pUserData)->m_Show = pResult->GetInteger(0) != 0;
}
void CChat::ConEcho(IConsole::IResult *pResult, void *pUserData)
{
((CChat *)pUserData)->Echo(pResult->GetString(0));
}
2020-12-22 18:24:48 +00:00
void CChat::ConchainChatOld(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
2020-10-13 20:08:52 +00:00
{
pfnCallback(pResult, pCallbackUserData);
((CChat *)pUserData)->RebuildChat();
}
void CChat::Echo(const char *pString)
{
AddLine(CLIENT_MSG, 0, pString);
}
2010-05-29 07:25:38 +00:00
void CChat::OnConsoleInit()
2008-08-27 19:41:02 +00:00
{
Console()->Register("say", "r[message]", CFGFLAG_CLIENT, ConSay, this, "Say in chat");
Console()->Register("say_team", "r[message]", CFGFLAG_CLIENT, ConSayTeam, this, "Say in team chat");
Console()->Register("chat", "s['team'|'all'] ?r[message]", CFGFLAG_CLIENT, ConChat, this, "Enable chat with all/team mode");
Console()->Register("+show_chat", "", CFGFLAG_CLIENT, ConShowChat, this, "Show chat");
Console()->Register("echo", "r[message]", CFGFLAG_CLIENT | CFGFLAG_STORE, ConEcho, this, "Echo the text in chat window");
2008-08-27 19:41:02 +00:00
}
void CChat::OnInit()
{
Reset();
Console()->Chain("cl_chat_old", ConchainChatOld, this);
}
bool CChat::OnInput(const IInput::CEvent &Event)
{
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_NONE)
return false;
2020-09-22 16:02:03 +00:00
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_ESCAPE)
{
DisableMode();
m_pClient->OnRelease();
if(g_Config.m_ClChatReset)
m_Input.Clear();
}
2020-09-22 16:02:03 +00:00
else if(Event.m_Flags & IInput::FLAG_PRESS && (Event.m_Key == KEY_RETURN || Event.m_Key == KEY_KP_ENTER))
{
2010-05-29 07:25:38 +00:00
if(m_Input.GetString()[0])
2011-03-20 15:17:06 +00:00
{
bool AddEntry = false;
2020-09-22 16:02:03 +00:00
if(m_LastChatSend + time_freq() < time())
{
2012-01-09 22:13:51 +00:00
Say(m_Mode == MODE_ALL ? 0 : 1, m_Input.GetString());
AddEntry = true;
}
else if(m_PendingChatCounter < 3)
{
2012-01-09 22:13:51 +00:00
++m_PendingChatCounter;
AddEntry = true;
}
if(AddEntry)
{
2020-09-22 16:02:03 +00:00
CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry) + m_Input.GetLength());
pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1;
2020-09-22 16:02:03 +00:00
mem_copy(pEntry->m_aText, m_Input.GetString(), m_Input.GetLength() + 1);
}
2011-03-20 15:17:06 +00:00
}
m_pHistoryEntry = 0x0;
DisableMode();
m_pClient->OnRelease();
m_Input.Clear();
}
2020-09-22 16:02:03 +00:00
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_TAB)
{
const bool ShiftPressed = Input()->ShiftIsPressed();
// fill the completion buffer
if(!m_CompletionUsed)
{
2020-09-22 16:02:03 +00:00
const char *pCursor = m_Input.GetString() + m_Input.GetCursorOffset();
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
for(size_t Count = 0; Count < m_Input.GetCursorOffset() && *(pCursor - 1) != ' '; --pCursor, ++Count)
2020-09-22 16:02:03 +00:00
;
m_PlaceholderOffset = pCursor - m_Input.GetString();
for(m_PlaceholderLength = 0; *pCursor && *pCursor != ' '; ++pCursor)
++m_PlaceholderLength;
2020-10-26 11:56:21 +00:00
str_truncate(m_aCompletionBuffer, sizeof(m_aCompletionBuffer), m_Input.GetString() + m_PlaceholderOffset, m_PlaceholderLength);
}
if(!m_CompletionUsed && m_aCompletionBuffer[0] != '/')
{
// Create the completion list of player names through which the player can iterate
const char *PlayerName, *FoundInput;
m_PlayerCompletionListLength = 0;
for(auto &PlayerInfo : m_pClient->m_Snap.m_apInfoByName)
{
if(PlayerInfo)
{
PlayerName = m_pClient->m_aClients[PlayerInfo->m_ClientID].m_aName;
FoundInput = str_utf8_find_nocase(PlayerName, m_aCompletionBuffer);
if(FoundInput != 0)
{
m_aPlayerCompletionList[m_PlayerCompletionListLength].ClientID = PlayerInfo->m_ClientID;
// The score for suggesting a player name is determined by the distance of the search input to the beginning of the player name
m_aPlayerCompletionList[m_PlayerCompletionListLength].Score = (int)(FoundInput - PlayerName);
m_PlayerCompletionListLength++;
}
}
}
std::stable_sort(m_aPlayerCompletionList, m_aPlayerCompletionList + m_PlayerCompletionListLength,
[](const CRateablePlayer &p1, const CRateablePlayer &p2) -> bool {
return p1.Score < p2.Score;
});
}
if(m_aCompletionBuffer[0] == '/')
{
CCommand *pCompletionCommand = 0;
const size_t NumCommands = m_vCommands.size();
if(ShiftPressed && m_CompletionUsed)
m_CompletionChosen--;
else if(!ShiftPressed)
m_CompletionChosen++;
m_CompletionChosen = (m_CompletionChosen + 2 * NumCommands) % (2 * NumCommands);
m_CompletionUsed = true;
2020-09-22 16:02:03 +00:00
const char *pCommandStart = m_aCompletionBuffer + 1;
for(size_t i = 0; i < 2 * NumCommands; ++i)
{
int SearchType;
int Index;
if(ShiftPressed)
{
2020-09-22 16:02:03 +00:00
SearchType = ((m_CompletionChosen - i + 2 * NumCommands) % (2 * NumCommands)) / NumCommands;
Index = (m_CompletionChosen - i + NumCommands) % NumCommands;
}
else
{
2020-09-22 16:02:03 +00:00
SearchType = ((m_CompletionChosen + i) % (2 * NumCommands)) / NumCommands;
Index = (m_CompletionChosen + i) % NumCommands;
}
auto &Command = m_vCommands[Index];
if(str_startswith(Command.m_pName, pCommandStart))
{
pCompletionCommand = &Command;
2020-09-22 16:02:03 +00:00
m_CompletionChosen = Index + SearchType * NumCommands;
break;
}
}
// insert the command
if(pCompletionCommand)
{
char aBuf[256];
// add part before the name
2020-10-26 11:56:21 +00:00
str_truncate(aBuf, sizeof(aBuf), m_Input.GetString(), m_PlaceholderOffset);
// add the command
str_append(aBuf, "/", sizeof(aBuf));
str_append(aBuf, pCompletionCommand->m_pName, sizeof(aBuf));
// add separator
const char *pSeparator = pCompletionCommand->m_pParams[0] == '\0' ? "" : " ";
str_append(aBuf, pSeparator, sizeof(aBuf));
if(*pSeparator)
str_append(aBuf, pSeparator, sizeof(aBuf));
// add part after the name
2020-09-22 16:02:03 +00:00
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionCommand->m_pName) + 1;
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.Set(aBuf);
2020-09-22 16:02:03 +00:00
m_Input.SetCursorOffset(m_PlaceholderOffset + m_PlaceholderLength);
}
}
else
{
// find next possible name
const char *pCompletionString = 0;
if(m_PlayerCompletionListLength > 0)
{
// We do this in a loop, if a player left the game during the repeated pressing of Tab, they are skipped
CGameClient::CClientData *pCompletionClientData;
for(int i = 0; i < m_PlayerCompletionListLength; ++i)
{
if(ShiftPressed && m_CompletionUsed)
{
m_CompletionChosen--;
}
else if(!ShiftPressed)
{
m_CompletionChosen++;
}
if(m_CompletionChosen < 0)
{
m_CompletionChosen += m_PlayerCompletionListLength;
}
m_CompletionChosen %= m_PlayerCompletionListLength;
m_CompletionUsed = true;
pCompletionClientData = &m_pClient->m_aClients[m_aPlayerCompletionList[m_CompletionChosen].ClientID];
if(!pCompletionClientData->m_Active)
{
continue;
}
pCompletionString = pCompletionClientData->m_aName;
break;
}
}
// insert the name
if(pCompletionString)
{
char aBuf[256];
// add part before the name
2020-10-26 11:56:21 +00:00
str_truncate(aBuf, sizeof(aBuf), m_Input.GetString(), m_PlaceholderOffset);
// add the name
str_append(aBuf, pCompletionString, sizeof(aBuf));
// add separator
const char *pSeparator = "";
2020-09-22 16:02:03 +00:00
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
2020-09-22 16:02:03 +00:00
str_append(aBuf, m_Input.GetString() + m_PlaceholderOffset + m_PlaceholderLength, sizeof(aBuf));
2020-09-22 16:02:03 +00:00
m_PlaceholderLength = str_length(pSeparator) + str_length(pCompletionString);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.Set(aBuf);
2020-09-22 16:02:03 +00:00
m_Input.SetCursorOffset(m_PlaceholderOffset + m_PlaceholderLength);
}
}
}
else
{
// reset name completion process
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key != KEY_TAB && Event.m_Key != KEY_LSHIFT && Event.m_Key != KEY_RSHIFT)
{
m_CompletionChosen = -1;
m_CompletionUsed = false;
}
2011-03-20 15:36:10 +00:00
m_Input.ProcessInput(Event);
}
2020-09-22 16:02:03 +00:00
if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_UP)
2011-03-20 15:17:06 +00:00
{
2012-01-09 22:13:51 +00:00
if(m_pHistoryEntry)
2011-03-20 15:17:06 +00:00
{
2012-01-09 22:13:51 +00:00
CHistoryEntry *pTest = m_History.Prev(m_pHistoryEntry);
2011-03-20 15:17:06 +00:00
2012-01-09 22:13:51 +00:00
if(pTest)
2011-03-20 15:17:06 +00:00
m_pHistoryEntry = pTest;
}
else
m_pHistoryEntry = m_History.Last();
2012-01-09 22:13:51 +00:00
if(m_pHistoryEntry)
m_Input.Set(m_pHistoryEntry->m_aText);
2011-03-20 15:17:06 +00:00
}
2020-09-22 16:02:03 +00:00
else if(Event.m_Flags & IInput::FLAG_PRESS && Event.m_Key == KEY_DOWN)
2011-03-20 15:17:06 +00:00
{
2012-01-09 22:13:51 +00:00
if(m_pHistoryEntry)
2011-03-20 15:17:06 +00:00
m_pHistoryEntry = m_History.Next(m_pHistoryEntry);
2018-03-13 20:50:49 +00:00
if(m_pHistoryEntry)
2012-01-09 22:13:51 +00:00
m_Input.Set(m_pHistoryEntry->m_aText);
2011-03-20 15:17:06 +00:00
else
m_Input.Clear();
}
return true;
}
2010-05-29 07:25:38 +00:00
void CChat::EnableMode(int Team)
{
if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
return;
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_NONE)
{
2010-05-29 07:25:38 +00:00
if(Team)
m_Mode = MODE_TEAM;
else
2010-05-29 07:25:38 +00:00
m_Mode = MODE_ALL;
Input()->Clear();
m_CompletionChosen = -1;
m_CompletionUsed = false;
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.Activate(EInputPriority::CHAT);
}
}
void CChat::DisableMode()
{
if(m_Mode != MODE_NONE)
{
m_Mode = MODE_NONE;
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.Deactivate();
}
}
2010-05-29 07:25:38 +00:00
void CChat::OnMessage(int MsgType, void *pRawMsg)
{
if(m_pClient->m_SuppressEvents)
return;
2010-05-29 07:25:38 +00:00
if(MsgType == NETMSGTYPE_SV_CHAT)
{
2010-05-29 07:25:38 +00:00
CNetMsg_Sv_Chat *pMsg = (CNetMsg_Sv_Chat *)pRawMsg;
AddLine(pMsg->m_ClientID, pMsg->m_Team, pMsg->m_pMessage);
}
}
2015-07-22 13:37:00 +00:00
bool CChat::LineShouldHighlight(const char *pLine, const char *pName)
{
const char *pHL = str_utf8_find_nocase(pLine, pName);
2015-07-22 13:37:00 +00:00
2018-03-13 20:50:49 +00:00
if(pHL)
2015-07-22 13:37:00 +00:00
{
2015-08-17 19:35:12 +00:00
int Length = str_length(pName);
2015-07-22 13:37:00 +00:00
2021-01-28 14:36:30 +00:00
if(Length > 0 && (pLine == pHL || pHL[-1] == ' ') && (pHL[Length] == 0 || pHL[Length] == ' ' || pHL[Length] == '.' || pHL[Length] == '!' || pHL[Length] == ',' || pHL[Length] == '?' || pHL[Length] == ':'))
2015-07-22 13:37:00 +00:00
return true;
}
return false;
}
#define SAVES_FILE "ddnet-saves.txt"
2020-06-22 20:31:41 +00:00
const char *SAVES_HEADER[] = {
"Time",
"Player",
"Map",
"Code",
};
void CChat::StoreSave(const char *pText)
{
const char *pStart = str_find(pText, "Team successfully saved by ");
const char *pMid = str_find(pText, ". Use '/load ");
const char *pOn = str_find(pText, "' on ");
const char *pEnd = str_find(pText, pOn ? " to continue" : "' to continue");
if(!pStart || !pMid || !pEnd || pMid < pStart || pEnd < pMid || (pOn && (pOn < pMid || pEnd < pOn)))
return;
char aName[16];
2020-10-26 11:56:21 +00:00
str_truncate(aName, sizeof(aName), pStart + 27, pMid - pStart - 27);
char aSaveCode[64];
2020-10-26 11:56:21 +00:00
str_truncate(aSaveCode, sizeof(aSaveCode), pMid + 13, (pOn ? pOn : pEnd) - pMid - 13);
char aTimestamp[20];
str_timestamp_format(aTimestamp, sizeof(aTimestamp), FORMAT_SPACE);
// TODO: Find a simple way to get the names of team members. This doesn't
// work since team is killed first, then save message gets sent:
/*
for(int i = 0; i < MAX_CLIENTS; i++)
{
const CNetObj_PlayerInfo *pInfo = GameClient()->m_Snap.m_paInfoByDDTeam[i];
if(!pInfo)
continue;
pInfo->m_Team // All 0
}
*/
2020-06-22 20:31:41 +00:00
IOHANDLE File = Storage()->OpenFile(SAVES_FILE, IOFLAG_APPEND, IStorage::TYPE_SAVE);
if(!File)
return;
2020-06-22 20:31:41 +00:00
const char *apColumns[4] = {
aTimestamp,
aName,
Client()->GetCurrentMap(),
aSaveCode,
};
if(io_tell(File) == 0)
{
2020-06-22 20:31:41 +00:00
CsvWrite(File, 4, SAVES_HEADER);
}
2020-06-22 20:31:41 +00:00
CsvWrite(File, 4, apColumns);
io_close(File);
}
void CChat::AddLine(int ClientID, int Team, const char *pLine)
{
if(*pLine == 0 ||
(ClientID == SERVER_MSG && !g_Config.m_ClShowChatSystem) ||
(ClientID >= 0 && (m_pClient->m_aClients[ClientID].m_aName[0] == '\0' || // unknown client
2020-09-22 16:02:03 +00:00
m_pClient->m_aClients[ClientID].m_ChatIgnore ||
(m_pClient->m_Snap.m_LocalClientID != ClientID && g_Config.m_ClShowChatFriends && !m_pClient->m_aClients[ClientID].m_Friend) ||
(m_pClient->m_Snap.m_LocalClientID != ClientID && m_pClient->m_aClients[ClientID].m_Foe))))
return;
2013-12-27 03:09:45 +00:00
// trim right and set maximum length to 256 utf8-characters
2013-04-01 18:30:58 +00:00
int Length = 0;
const char *pStr = pLine;
const char *pEnd = 0;
while(*pStr)
2015-07-09 00:08:14 +00:00
{
2013-04-01 18:30:58 +00:00
const char *pStrOld = pStr;
int Code = str_utf8_decode(&pStr);
// check if unicode is not empty
if(!str_utf8_isspace(Code))
2013-04-01 18:30:58 +00:00
{
pEnd = 0;
}
else if(pEnd == 0)
pEnd = pStrOld;
2013-12-27 03:09:45 +00:00
if(++Length >= 256)
2013-04-01 18:30:58 +00:00
{
*(const_cast<char *>(pStr)) = 0;
break;
}
2015-07-09 00:08:14 +00:00
}
2013-04-01 18:30:58 +00:00
if(pEnd != 0)
*(const_cast<char *>(pEnd)) = 0;
bool Highlighted = false;
2020-09-22 16:02:03 +00:00
char *p = const_cast<char *>(pLine);
// Only empty string left
if(*p == 0)
return;
auto &&FChatMsgCheckAndPrint = [this](CLine *pLine_) {
if(pLine_->m_ClientID < 0) // server or client message
2021-03-01 17:35:06 +00:00
{
if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
StoreSave(pLine_->m_aText);
2021-03-01 17:35:06 +00:00
}
char aBuf[1024];
str_format(aBuf, sizeof(aBuf), "%s%s%s", pLine_->m_aName, pLine_->m_ClientID >= 0 ? ": " : "", pLine_->m_aText);
2021-03-08 00:08:38 +00:00
ColorRGBA ChatLogColor{1, 1, 1, 1};
if(pLine_->m_Highlighted)
2021-03-08 00:08:38 +00:00
{
ChatLogColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageHighlightColor));
}
else
{
if(pLine_->m_Friend && g_Config.m_ClMessageFriend)
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageFriendColor));
else if(pLine_->m_Team)
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageTeamColor));
else if(pLine_->m_ClientID == SERVER_MSG)
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageSystemColor));
else if(pLine_->m_ClientID == CLIENT_MSG)
2021-03-08 00:08:38 +00:00
ChatLogColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageClientColor));
else // regular message
ChatLogColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageColor));
}
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, pLine_->m_Whisper ? "whisper" : (pLine_->m_Team ? "teamchat" : "chat"), aBuf, ChatLogColor);
2021-03-01 17:35:06 +00:00
};
2010-05-29 07:25:38 +00:00
while(*p)
{
Highlighted = false;
2010-05-29 07:25:38 +00:00
pLine = p;
2018-07-10 09:29:02 +00:00
// find line separator and strip multiline
2010-05-29 07:25:38 +00:00
while(*p)
{
if(*p++ == '\n')
{
2020-09-22 16:02:03 +00:00
*(p - 1) = 0;
2010-05-29 07:25:38 +00:00
break;
}
}
2020-09-20 15:25:27 +00:00
CLine *pCurrentLine = &m_aLines[m_CurrentLine];
2022-03-29 20:39:16 +00:00
// Team Number:
// 0 = global; 1 = team; 2 = sending whisper; 3 = receiving whisper
2020-09-20 15:25:27 +00:00
// If it's a client message, m_aText will have ": " prepended so we have to work around it.
if(pCurrentLine->m_TeamNumber == Team && pCurrentLine->m_ClientID == ClientID && str_comp(pCurrentLine->m_aText, pLine) == 0)
2020-09-20 15:25:27 +00:00
{
pCurrentLine->m_TimesRepeated++;
TextRender()->DeleteTextContainer(pCurrentLine->m_TextContainerIndex);
Graphics()->DeleteQuadContainer(pCurrentLine->m_QuadContainerIndex);
2020-09-20 15:25:27 +00:00
pCurrentLine->m_Time = time();
pCurrentLine->m_aYOffset[0] = -1.f;
pCurrentLine->m_aYOffset[1] = -1.f;
2021-03-01 17:35:06 +00:00
FChatMsgCheckAndPrint(pCurrentLine);
return;
2020-09-20 15:25:27 +00:00
}
2020-09-22 16:02:03 +00:00
m_CurrentLine = (m_CurrentLine + 1) % MAX_LINES;
2011-08-11 08:59:14 +00:00
2020-09-20 15:25:27 +00:00
pCurrentLine = &m_aLines[m_CurrentLine];
pCurrentLine->m_TimesRepeated = 0;
2020-09-20 15:25:27 +00:00
pCurrentLine->m_Time = time();
pCurrentLine->m_aYOffset[0] = -1.0f;
pCurrentLine->m_aYOffset[1] = -1.0f;
2020-09-20 15:25:27 +00:00
pCurrentLine->m_ClientID = ClientID;
pCurrentLine->m_TeamNumber = Team;
pCurrentLine->m_Team = Team == 1;
pCurrentLine->m_Whisper = Team >= 2;
2020-09-20 15:25:27 +00:00
pCurrentLine->m_NameColor = -2;
TextRender()->DeleteTextContainer(pCurrentLine->m_TextContainerIndex);
Graphics()->DeleteQuadContainer(pCurrentLine->m_QuadContainerIndex);
2020-10-13 20:08:52 +00:00
// check for highlighted name
2018-03-13 20:50:49 +00:00
if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
{
if(ClientID >= 0 && ClientID != m_pClient->m_aLocalIDs[0] && (!m_pClient->Client()->DummyConnected() || ClientID != m_pClient->m_aLocalIDs[1]))
2015-08-20 14:01:34 +00:00
{
// main character
Highlighted |= LineShouldHighlight(pLine, m_pClient->m_aClients[m_pClient->m_aLocalIDs[0]].m_aName);
2015-08-20 14:01:34 +00:00
// dummy
Highlighted |= m_pClient->Client()->DummyConnected() && LineShouldHighlight(pLine, m_pClient->m_aClients[m_pClient->m_aLocalIDs[1]].m_aName);
2015-08-20 14:01:34 +00:00
}
}
2015-07-22 13:37:00 +00:00
else
2014-05-17 20:45:44 +00:00
{
2015-07-22 13:37:00 +00:00
// on demo playback use local id from snap directly,
// since m_aLocalIDs isn't valid there
Highlighted |= m_pClient->m_Snap.m_LocalClientID >= 0 && LineShouldHighlight(pLine, m_pClient->m_aClients[m_pClient->m_Snap.m_LocalClientID].m_aName);
2014-05-17 20:45:44 +00:00
}
2020-09-20 15:25:27 +00:00
pCurrentLine->m_Highlighted = Highlighted;
2010-05-29 07:25:38 +00:00
if(pCurrentLine->m_ClientID == SERVER_MSG)
2010-05-29 07:25:38 +00:00
{
2022-07-09 16:14:56 +00:00
str_copy(pCurrentLine->m_aName, "*** ");
str_copy(pCurrentLine->m_aText, pLine);
2010-05-29 07:25:38 +00:00
}
else if(pCurrentLine->m_ClientID == CLIENT_MSG)
{
str_copy(pCurrentLine->m_aName, "");
str_copy(pCurrentLine->m_aText, pLine);
}
2010-05-29 07:25:38 +00:00
else
{
if(m_pClient->m_aClients[ClientID].m_Team == TEAM_SPECTATORS)
2020-09-20 15:25:27 +00:00
pCurrentLine->m_NameColor = TEAM_SPECTATORS;
2010-05-29 07:25:38 +00:00
2020-09-22 16:02:03 +00:00
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags & GAMEFLAG_TEAMS)
2010-05-29 07:25:38 +00:00
{
if(m_pClient->m_aClients[ClientID].m_Team == TEAM_RED)
2020-09-20 15:25:27 +00:00
pCurrentLine->m_NameColor = TEAM_RED;
else if(m_pClient->m_aClients[ClientID].m_Team == TEAM_BLUE)
2020-09-20 15:25:27 +00:00
pCurrentLine->m_NameColor = TEAM_BLUE;
2010-05-29 07:25:38 +00:00
}
2018-03-13 20:50:49 +00:00
if(Team == 2) // whisper send
{
2020-09-20 15:25:27 +00:00
str_format(pCurrentLine->m_aName, sizeof(pCurrentLine->m_aName), "→ %s", m_pClient->m_aClients[ClientID].m_aName);
pCurrentLine->m_NameColor = TEAM_BLUE;
pCurrentLine->m_Highlighted = false;
Highlighted = false;
}
2018-03-13 20:50:49 +00:00
else if(Team == 3) // whisper recv
{
2020-09-20 15:25:27 +00:00
str_format(pCurrentLine->m_aName, sizeof(pCurrentLine->m_aName), "← %s", m_pClient->m_aClients[ClientID].m_aName);
pCurrentLine->m_NameColor = TEAM_RED;
pCurrentLine->m_Highlighted = true;
Highlighted = true;
}
else
str_copy(pCurrentLine->m_aName, m_pClient->m_aClients[ClientID].m_aName);
str_copy(pCurrentLine->m_aText, pLine);
2020-09-20 15:25:27 +00:00
pCurrentLine->m_Friend = m_pClient->m_aClients[ClientID].m_Friend;
}
2020-10-13 20:08:52 +00:00
pCurrentLine->m_HasRenderTee = false;
2020-09-20 15:25:27 +00:00
pCurrentLine->m_Friend = ClientID >= 0 ? m_pClient->m_aClients[ClientID].m_Friend : false;
2020-10-13 20:08:52 +00:00
if(pCurrentLine->m_ClientID >= 0 && pCurrentLine->m_aName[0] != '\0')
{
if(!g_Config.m_ClChatOld)
2020-10-13 20:08:52 +00:00
{
pCurrentLine->m_CustomColoredSkin = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_CustomColoredSkin;
if(pCurrentLine->m_CustomColoredSkin)
pCurrentLine->m_RenderSkin = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorableRenderSkin;
else
pCurrentLine->m_RenderSkin = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_OriginalRenderSkin;
2022-07-09 16:14:56 +00:00
str_copy(pCurrentLine->m_aSkinName, m_pClient->m_aClients[pCurrentLine->m_ClientID].m_aSkinName);
2020-10-13 20:08:52 +00:00
pCurrentLine->m_ColorBody = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorBody;
pCurrentLine->m_ColorFeet = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_ColorFeet;
2020-11-08 05:39:16 +00:00
pCurrentLine->m_RenderSkinMetrics = m_pClient->m_aClients[pCurrentLine->m_ClientID].m_RenderInfo.m_SkinMetrics;
2020-10-13 20:08:52 +00:00
pCurrentLine->m_HasRenderTee = true;
}
}
2021-03-01 17:35:06 +00:00
FChatMsgCheckAndPrint(pCurrentLine);
}
// play sound
2021-06-23 05:05:49 +00:00
int64_t Now = time();
if(ClientID == SERVER_MSG)
{
2020-09-22 16:02:03 +00:00
if(Now - m_aLastSoundPlayed[CHAT_SERVER] >= time_freq() * 3 / 10)
{
if(g_Config.m_SndServerMessage)
{
2021-07-12 09:43:56 +00:00
m_pClient->m_Sounds.Play(CSounds::CHN_GUI, SOUND_CHAT_SERVER, 0);
m_aLastSoundPlayed[CHAT_SERVER] = Now;
}
}
}
else if(ClientID == CLIENT_MSG)
{
// No sound yet
}
else if(Highlighted && Client()->State() != IClient::STATE_DEMOPLAYBACK)
{
2020-09-22 16:02:03 +00:00
if(Now - m_aLastSoundPlayed[CHAT_HIGHLIGHT] >= time_freq() * 3 / 10)
{
2015-08-11 01:10:05 +00:00
char aBuf[1024];
2020-10-13 20:08:52 +00:00
str_format(aBuf, sizeof(aBuf), "%s: %s", m_aLines[m_CurrentLine].m_aName, m_aLines[m_CurrentLine].m_aText);
Client()->Notify("DDNet Chat", aBuf);
if(g_Config.m_SndHighlight)
{
2021-07-12 09:43:56 +00:00
m_pClient->m_Sounds.Play(CSounds::CHN_GUI, SOUND_CHAT_HIGHLIGHT, 0);
m_aLastSoundPlayed[CHAT_HIGHLIGHT] = Now;
}
2019-04-05 23:15:02 +00:00
if(g_Config.m_ClEditor)
{
GameClient()->Editor()->UpdateMentions();
}
}
}
else if(Team != 2)
{
2020-09-22 16:02:03 +00:00
if(Now - m_aLastSoundPlayed[CHAT_CLIENT] >= time_freq() * 3 / 10)
{
bool PlaySound = m_aLines[m_CurrentLine].m_Team ? g_Config.m_SndTeamChat : g_Config.m_SndChat;
#if defined(CONF_VIDEORECORDER)
if(IVideo::Current())
{
PlaySound &= (bool)g_Config.m_ClVideoShowChat;
}
#endif
if(PlaySound)
2014-05-04 16:35:37 +00:00
{
2021-07-12 09:43:56 +00:00
m_pClient->m_Sounds.Play(CSounds::CHN_GUI, SOUND_CHAT_CLIENT, 0);
2014-05-04 16:35:37 +00:00
m_aLastSoundPlayed[CHAT_CLIENT] = Now;
}
}
}
}
2020-10-13 20:08:52 +00:00
void CChat::RefindSkins()
{
2021-02-15 14:15:21 +00:00
for(auto &Line : m_aLines)
2020-10-13 20:08:52 +00:00
{
2021-02-15 14:15:21 +00:00
if(Line.m_HasRenderTee)
2020-10-13 20:08:52 +00:00
{
const CSkin *pSkin = m_pClient->m_Skins.Find(Line.m_aSkinName);
2021-02-15 14:15:21 +00:00
if(Line.m_CustomColoredSkin)
Line.m_RenderSkin = pSkin->m_ColorableSkin;
2020-10-13 20:08:52 +00:00
else
2021-02-15 14:15:21 +00:00
Line.m_RenderSkin = pSkin->m_OriginalSkin;
2020-11-08 05:39:16 +00:00
2021-02-15 14:15:21 +00:00
Line.m_RenderSkinMetrics = pSkin->m_Metrics;
2020-10-13 20:08:52 +00:00
}
}
}
2018-03-13 20:50:49 +00:00
void CChat::OnPrepareLines()
{
float x = 5.0f;
float y = 300.0f - 28.0f;
2020-10-13 20:08:52 +00:00
float FontSize = FONT_SIZE;
2018-03-13 20:50:49 +00:00
float ScreenRatio = Graphics()->ScreenAspect();
2021-07-12 09:43:56 +00:00
bool IsScoreBoardOpen = m_pClient->m_Scoreboard.Active() && (ScreenRatio > 1.7f); // only assume scoreboard when screen ratio is widescreen(something around 16:9)
2020-11-01 03:52:07 +00:00
bool ForceRecreate = IsScoreBoardOpen != m_PrevScoreBoardShowed;
bool ShowLargeArea = m_Show || g_Config.m_ClShowChat == 2;
2020-10-13 20:08:52 +00:00
ForceRecreate |= ShowLargeArea != m_PrevShowChat;
2020-11-01 03:52:07 +00:00
m_PrevScoreBoardShowed = IsScoreBoardOpen;
m_PrevShowChat = ShowLargeArea;
2018-03-13 20:50:49 +00:00
2020-10-13 20:08:52 +00:00
float RealMsgPaddingX = MESSAGE_PADDING_X;
float RealMsgPaddingY = MESSAGE_PADDING_Y;
float RealMsgPaddingTee = MESSAGE_TEE_SIZE + MESSAGE_TEE_PADDING_RIGHT;
if(g_Config.m_ClChatOld)
2020-10-13 20:08:52 +00:00
{
RealMsgPaddingX = 0;
RealMsgPaddingY = 0;
2020-10-13 20:08:52 +00:00
RealMsgPaddingTee = 0;
}
2020-10-13 20:08:52 +00:00
2021-06-23 05:05:49 +00:00
int64_t Now = time();
float LineWidth = (IsScoreBoardOpen ? 85.0f : 200.0f) - (RealMsgPaddingX * 1.5f) - RealMsgPaddingTee;
2020-10-13 20:08:52 +00:00
2020-11-01 03:52:07 +00:00
float HeightLimit = IsScoreBoardOpen ? 180.0f : m_PrevShowChat ? 50.0f : 200.0f;
2018-03-13 20:50:49 +00:00
float Begin = x;
2020-10-13 20:08:52 +00:00
float TextBegin = Begin + RealMsgPaddingX / 2.0f;
2018-03-13 20:50:49 +00:00
CTextCursor Cursor;
2020-11-01 03:52:07 +00:00
int OffsetType = IsScoreBoardOpen ? 1 : 0;
2020-10-13 20:08:52 +00:00
2018-03-13 20:50:49 +00:00
for(int i = 0; i < MAX_LINES; i++)
{
int r = ((m_CurrentLine - i) + MAX_LINES) % MAX_LINES;
2020-10-13 20:08:52 +00:00
if(Now > m_aLines[r].m_Time + 16 * time_freq() && !m_PrevShowChat)
2018-03-13 20:50:49 +00:00
break;
if(m_aLines[r].m_TextContainerIndex != -1 && !ForceRecreate)
continue;
TextRender()->DeleteTextContainer(m_aLines[r].m_TextContainerIndex);
Graphics()->DeleteQuadContainer(m_aLines[r].m_QuadContainerIndex);
2020-10-13 20:08:52 +00:00
char aName[64 + 12] = "";
2020-09-20 15:25:27 +00:00
2019-05-10 22:26:15 +00:00
if(g_Config.m_ClShowIDs && m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
2018-03-13 20:50:49 +00:00
{
2020-10-13 20:08:52 +00:00
if(m_aLines[r].m_ClientID < 10)
str_format(aName, sizeof(aName), "%d: ", m_aLines[r].m_ClientID);
2018-03-13 20:50:49 +00:00
else
2020-10-13 20:08:52 +00:00
str_format(aName, sizeof(aName), "%d: ", m_aLines[r].m_ClientID);
2018-03-13 20:50:49 +00:00
}
2020-09-20 15:25:27 +00:00
str_append(aName, m_aLines[r].m_aName, sizeof(aName));
char aCount[12];
if(m_aLines[r].m_ClientID < 0)
str_format(aCount, sizeof(aCount), "[%d] ", m_aLines[r].m_TimesRepeated + 1);
else
str_format(aCount, sizeof(aCount), " [%d]", m_aLines[r].m_TimesRepeated + 1);
2018-03-13 20:50:49 +00:00
if(g_Config.m_ClChatOld)
2020-10-13 20:08:52 +00:00
{
m_aLines[r].m_HasRenderTee = false;
}
2018-03-13 20:50:49 +00:00
// get the y offset (calculate it if we haven't done that yet)
if(m_aLines[r].m_aYOffset[OffsetType] < 0.0f)
2018-03-13 20:50:49 +00:00
{
2020-10-13 20:08:52 +00:00
TextRender()->SetCursor(&Cursor, TextBegin, 0.0f, FontSize, 0);
2018-03-13 20:50:49 +00:00
Cursor.m_LineWidth = LineWidth;
2020-09-20 15:25:27 +00:00
2020-10-22 20:21:19 +00:00
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
2020-10-13 20:08:52 +00:00
{
2020-10-22 20:21:19 +00:00
Cursor.m_X += RealMsgPaddingTee;
if(m_aLines[r].m_Friend && g_Config.m_ClMessageFriend)
{
TextRender()->TextEx(&Cursor, "", -1);
}
2020-10-13 20:08:52 +00:00
}
2018-03-13 20:50:49 +00:00
TextRender()->TextEx(&Cursor, aName, -1);
if(m_aLines[r].m_TimesRepeated > 0)
TextRender()->TextEx(&Cursor, aCount, -1);
2020-10-13 20:08:52 +00:00
2020-10-22 20:21:19 +00:00
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
{
TextRender()->TextEx(&Cursor, ": ", -1);
}
2020-10-13 20:08:52 +00:00
CTextCursor AppendCursor = Cursor;
AppendCursor.m_LongestLineWidth = 0.0f;
2021-10-14 14:49:11 +00:00
if(!IsScoreBoardOpen && !g_Config.m_ClChatOld)
2020-11-01 03:52:07 +00:00
{
AppendCursor.m_StartX = Cursor.m_X;
AppendCursor.m_LineWidth -= Cursor.m_LongestLineWidth;
2020-11-01 03:52:07 +00:00
}
2020-10-13 20:08:52 +00:00
TextRender()->TextEx(&AppendCursor, m_aLines[r].m_aText, -1);
m_aLines[r].m_aYOffset[OffsetType] = AppendCursor.m_Y + AppendCursor.m_FontSize + RealMsgPaddingY;
2018-03-13 20:50:49 +00:00
}
2020-10-13 20:08:52 +00:00
y -= m_aLines[r].m_aYOffset[OffsetType];
2018-03-13 20:50:49 +00:00
// cut off if msgs waste too much space
if(y < HeightLimit)
break;
// the position the text was created
2020-10-13 20:08:52 +00:00
m_aLines[r].m_TextYOffset = y + RealMsgPaddingY / 2.f;
2018-03-13 20:50:49 +00:00
2020-10-22 20:21:19 +00:00
int CurRenderFlags = TextRender()->GetRenderFlags();
TextRender()->SetRenderFlags(CurRenderFlags | ETextRenderFlags::TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD);
2018-03-13 20:50:49 +00:00
// reset the cursor
2020-10-13 20:08:52 +00:00
TextRender()->SetCursor(&Cursor, TextBegin, m_aLines[r].m_TextYOffset, FontSize, TEXTFLAG_RENDER);
2018-03-13 20:50:49 +00:00
Cursor.m_LineWidth = LineWidth;
2020-10-13 20:08:52 +00:00
// Message is from valid player
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
2018-03-13 20:50:49 +00:00
{
2020-10-13 20:08:52 +00:00
Cursor.m_X += RealMsgPaddingTee;
if(m_aLines[r].m_Friend && g_Config.m_ClMessageFriend)
{
const char *pHeartStr = "";
ColorRGBA rgb = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageFriendColor));
TextRender()->TextColor(rgb.WithAlpha(1.f));
TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &Cursor, pHeartStr);
2020-10-13 20:08:52 +00:00
}
2018-03-13 20:50:49 +00:00
}
// render name
2020-10-13 20:08:52 +00:00
ColorRGBA NameColor;
if(m_aLines[r].m_ClientID == SERVER_MSG)
2020-10-13 20:08:52 +00:00
NameColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageSystemColor));
else if(m_aLines[r].m_ClientID == CLIENT_MSG)
2020-10-13 20:08:52 +00:00
NameColor = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageClientColor));
2018-03-13 20:50:49 +00:00
else if(m_aLines[r].m_Team)
2020-10-13 20:08:52 +00:00
NameColor = CalculateNameColor(ColorHSLA(g_Config.m_ClMessageTeamColor));
2018-03-13 20:50:49 +00:00
else if(m_aLines[r].m_NameColor == TEAM_RED)
NameColor = ColorRGBA(1.0f, 0.5f, 0.5f, 1.f);
2018-03-13 20:50:49 +00:00
else if(m_aLines[r].m_NameColor == TEAM_BLUE)
NameColor = ColorRGBA(0.7f, 0.7f, 1.0f, 1.f);
2018-03-13 20:50:49 +00:00
else if(m_aLines[r].m_NameColor == TEAM_SPECTATORS)
NameColor = ColorRGBA(0.75f, 0.5f, 0.75f, 1.f);
2018-03-13 20:50:49 +00:00
else if(m_aLines[r].m_ClientID >= 0 && g_Config.m_ClChatTeamColors && m_pClient->m_Teams.Team(m_aLines[r].m_ClientID))
2020-10-13 20:08:52 +00:00
NameColor = color_cast<ColorRGBA>(ColorHSLA(m_pClient->m_Teams.Team(m_aLines[r].m_ClientID) / 64.0f, 1.0f, 0.75f));
2018-03-13 20:50:49 +00:00
else
2020-10-13 20:08:52 +00:00
NameColor = ColorRGBA(0.8f, 0.8f, 0.8f, 1.f);
TextRender()->TextColor(NameColor);
TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &Cursor, aName);
2018-03-13 20:50:49 +00:00
if(m_aLines[r].m_TimesRepeated > 0)
2018-03-13 20:50:49 +00:00
{
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 0.3f);
TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &Cursor, aCount);
2018-03-13 20:50:49 +00:00
}
2020-10-13 20:08:52 +00:00
if(m_aLines[r].m_ClientID >= 0 && m_aLines[r].m_aName[0] != '\0')
{
TextRender()->TextColor(NameColor);
TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &Cursor, ": ");
2020-10-13 20:08:52 +00:00
}
// render line
ColorRGBA Color;
if(m_aLines[r].m_ClientID == SERVER_MSG)
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageSystemColor));
else if(m_aLines[r].m_ClientID == CLIENT_MSG)
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageClientColor));
else if(m_aLines[r].m_Highlighted)
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageHighlightColor));
else if(m_aLines[r].m_Team)
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageTeamColor));
2018-03-13 20:50:49 +00:00
else // regular message
Color = color_cast<ColorRGBA>(ColorHSLA(g_Config.m_ClMessageColor));
TextRender()->TextColor(Color);
2018-03-13 20:50:49 +00:00
2020-10-13 20:08:52 +00:00
CTextCursor AppendCursor = Cursor;
AppendCursor.m_LongestLineWidth = 0.0f;
float OriginalWidth = 0.0f;
2021-10-14 14:49:11 +00:00
if(!IsScoreBoardOpen && !g_Config.m_ClChatOld)
2020-11-01 03:52:07 +00:00
{
AppendCursor.m_StartX = Cursor.m_X;
AppendCursor.m_LineWidth -= Cursor.m_LongestLineWidth;
OriginalWidth = Cursor.m_LongestLineWidth;
2020-11-01 03:52:07 +00:00
}
2020-10-13 20:08:52 +00:00
TextRender()->CreateOrAppendTextContainer(m_aLines[r].m_TextContainerIndex, &AppendCursor, m_aLines[r].m_aText);
2020-10-13 20:08:52 +00:00
if(!g_Config.m_ClChatOld && (m_aLines[r].m_aText[0] != '\0' || m_aLines[r].m_aName[0] != '\0'))
2020-10-13 20:08:52 +00:00
{
float Height = m_aLines[r].m_aYOffset[OffsetType];
2020-10-13 20:08:52 +00:00
Graphics()->SetColor(1, 1, 1, 1);
m_aLines[r].m_QuadContainerIndex = Graphics()->CreateRectQuadContainer(Begin, y, OriginalWidth + AppendCursor.m_LongestLineWidth + RealMsgPaddingX * 1.5f, Height, MESSAGE_ROUNDING, IGraphics::CORNER_ALL);
2020-10-13 20:08:52 +00:00
}
2020-10-22 20:21:19 +00:00
TextRender()->SetRenderFlags(CurRenderFlags);
if(m_aLines[r].m_TextContainerIndex != -1)
TextRender()->UploadTextContainer(m_aLines[r].m_TextContainerIndex);
2018-03-13 20:50:49 +00:00
}
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
2010-05-29 07:25:38 +00:00
void CChat::OnRender()
{
2012-01-09 22:13:51 +00:00
// send pending chat messages
2020-09-22 16:02:03 +00:00
if(m_PendingChatCounter > 0 && m_LastChatSend + time_freq() < time())
2012-01-09 22:13:51 +00:00
{
CHistoryEntry *pEntry = m_History.Last();
2020-09-22 16:02:03 +00:00
for(int i = m_PendingChatCounter - 1; pEntry; --i, pEntry = m_History.Prev(pEntry))
2012-01-09 22:13:51 +00:00
{
if(i == 0)
{
Say(pEntry->m_Team, pEntry->m_aText);
break;
}
}
--m_PendingChatCounter;
}
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
const float Height = 300.0f;
const float Width = Height * Graphics()->ScreenAspect();
Graphics()->MapScreen(0.0f, 0.0f, Width, Height);
float x = 5.0f;
2020-09-22 16:02:03 +00:00
float y = 300.0f - 20.0f;
2010-05-29 07:25:38 +00:00
if(m_Mode != MODE_NONE)
{
// render chat input
2010-05-29 07:25:38 +00:00
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, x, y, 8.0f, TEXTFLAG_RENDER);
2020-09-22 16:02:03 +00:00
Cursor.m_LineWidth = Width - 190.0f;
2010-05-29 07:25:38 +00:00
if(m_Mode == MODE_ALL)
TextRender()->TextEx(&Cursor, Localize("All"), -1);
else if(m_Mode == MODE_TEAM)
TextRender()->TextEx(&Cursor, Localize("Team"), -1);
else
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, Localize("Chat"), -1);
2009-06-15 07:34:25 +00:00
2010-05-29 07:25:38 +00:00
TextRender()->TextEx(&Cursor, ": ", -1);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
const float MessageMaxWidth = Cursor.m_LineWidth - (Cursor.m_X - Cursor.m_StartX);
const CUIRect ClippingRect = {Cursor.m_X, Cursor.m_Y, MessageMaxWidth, 2.25f * Cursor.m_FontSize};
const float XScale = Graphics()->ScreenWidth() / Width;
const float YScale = Graphics()->ScreenHeight() / Height;
Graphics()->ClipEnable((int)(ClippingRect.x * XScale), (int)(ClippingRect.y * YScale), (int)(ClippingRect.w * XScale), (int)(ClippingRect.h * YScale));
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
float ScrollOffset = m_Input.GetScrollOffset();
float ScrollOffsetChange = m_Input.GetScrollOffsetChange();
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.Activate(EInputPriority::CHAT); // Ensure that the input is active
const CUIRect InputCursorRect = {Cursor.m_X, Cursor.m_Y - ScrollOffset, 0.0f, 0.0f};
const STextBoundingBox BoundingBox = m_Input.Render(&InputCursorRect, Cursor.m_FontSize, TEXTALIGN_TL, m_Input.WasChanged(), MessageMaxWidth);
Graphics()->ClipDisable();
// Scroll up or down to keep the caret inside the clipping rect
const float CaretPositionY = m_Input.GetCaretPosition().y - ScrollOffsetChange;
if(CaretPositionY < ClippingRect.y)
ScrollOffsetChange -= ClippingRect.y - CaretPositionY;
else if(CaretPositionY + Cursor.m_FontSize > ClippingRect.y + ClippingRect.h)
ScrollOffsetChange += CaretPositionY + Cursor.m_FontSize - (ClippingRect.y + ClippingRect.h);
UI()->DoSmoothScrollLogic(&ScrollOffset, &ScrollOffsetChange, ClippingRect.h, BoundingBox.m_H);
Port line input and IME support from 0.7 Port the line input (UI edit boxes, chat, console) and Input Method Editor (IME) support from upstream. Closes #4397. General ------------------------------ Fix issues with the text input. Closes #4346. Closes #4524. Word skipping (when holding Ctrl) is overhauled to be consistent with the Windows / Firefox experience that I took as reference. Improve usability by not blinking (i.e. always rendering) the caret shortly after is has been moved. UI text input ------------------------------ Fix inconsistent mouse-based left and right scrolling (closes #4347). Support smooth left and right scrolling. Chat ------------------------------ Support keyboard-based text selection of the chat input. Mouse-based selection could be support in the future when we decide to add something like an ingame UI cursor. Support smooth up and down scrolling of the chat input, removing the old hack that offsets the input string to simulate scrolling. Console ------------------------------ Also support mouse-based text selection of the command input. Only text from either the command input or the console log can be selected at the same time. This ensures that Ctrl+C will always copy the text that is currently visually selected in the console. Check for Ctrl+C input event in event handler instead of in render function, to hopefully fix the issue that copying does not work sometimes (closes #5974 until further notice). When Ctrl+C is used to copy text from the console log, the selection is cleared. This should make it more clear when text was copied from the log. Fix an issue that was preventing the console log selection from being cleared, when all log lines are selected. Remove Ctrl+A/E hotkeys that move cursor to beginning/end respectively. Ctrl+A now selectes all text like for all other inputs. Home and End keys can still be used to go the beginning and end. Remove Ctrl+U/K hotkeys that clear everything before/after the cursor respectively. Hold shift and use Home/End to select everything instead. IME support ------------------------------ Render list of IME candidates in the client on Windows, so the candidate list can also be viewed in fullscreen mode. There is no API available to retrieve a candidate list on the other operating systems. Improve composition rendering by underlining the composition text instead of putting it in square brackets. Track active input globally to properly activate and deactivate IME through the SDL functions. Closes #1030. Closes #1008. Password rendering ------------------------------ Fix rendering of passwords containing unicode. Instead of rendering one star character for each UTF-8 `char`, render on star for every unicode codepoint. Show the composition text also for passwords. Without seeing the composition text it's hard to type a password containing those characters. The candidate window exposes the composition anyway. If you don't want to expose your password this way, e.g. while streaming, you could: 1. Use a latin password and switch off the IME for the password input with the IME hotkey. 2. Blank your screen with an external program while you are streaming and entering passwords. 3. Use binds to authenticate in rcon or to set the server browser password. Refactoring ------------------------------ Move all text input logic and general rendering to `CLineInput`. A `CLineInput` is associated with a particular `char` buffer given as a pointer either in the constructor or with `SetBuffer`. The maximum byte size of the buffer must also be specified. The maximum length in unicode codepoints can also be specified separately (e.g. on upstream, name are limited by the number of unicode codepoints instead). Add `CLineInputBuffered`, which is a `CLineInput` that own a `char` buffer of a fixed size, which is specified as a template argument. As `CLineInput` does not own a buffer anymore, this reduces duplicate code for line inputs that need their own buffer. Add `CLineInputNumber` which has additional convenience functions to consider the text as an `int` or `float`, to reduce duplicate code in those cases. In the future we could also add an input filter function so that only numbers can be entered in the number input. Add `CLineInput::SetClipboardLineCallback` to handle the case that multiple lines of text are pasted into a lineinput. This reduces duplicate code, as this behavior was previously implemented separately for chat and console. The behavior is also fixed to be consistent with the console on Windows, so the first line being pasted edits the current input text and then sends it instead of being sent on its own without the existing input text. Add `CalcFontSizeAndBoundingBox` to UI to reduce duplicate code. Expose `CalcAlignedCursorPos` as static member function to reuse it for line input. Dispatch input events to UI inputs through the event handler instead of storing them in a duplicate buffer. Use `size_t` for line input cursor position, length etc. and for `str_utf8_stats`. Add `IButtonColorFunction` to UI to describe a functions that defines colors for the Default, Active and Hovered states of UI elements. Add some default button color functions. Use button color function to reduce duplicate code in scrollbar rendering. Use `vec2` instead of two `floats` to represent the mouse positions in the text renderer. Remove `CaretPosition` again, as it does not calculate the correct Y position near line breaks due to the wrapping being different when not rendering the entire string. Instead, calculate the exact caret position when rending a text container and store the caret position in the text cursor for later use. IME usage guide (Windows) ------------------------------ 1. Install the respective language and the Microsoft-IME keyboard (e.g. for Chinese, Japanese or Korean). 2. Launch the game (or a text editor to first try out the IME). Note that Windows may track the input language separately for every application. You can change this in the Windows input settings so the input language is changed globally. 2. Switch the input language using the hotkey Windows+Space or another hotkey that you configured in the Windows input settings (Alt+Shift is the default, but you should consider disabling it, to avoid accidentally changing the input language while playing). 3. Switch from Latin/English input mode to the respective asian input mode. - Chinese: Use Ctrl+Space to switch between English and Chinese input mode. You can change this hotkey in the IME's settings. - Japanese: Use Ctrl+Space to switch between Alphanumeric and Hiragana/Katakana input mode. You can change this hotkey in the IME's settings. - Korean: Use Right Alt to switch between English and Hangul input mode. You cannot change this hotkey as of yet. - Note that the input mode is also tracked per application, but there is no setting to change this behavior as far as I know, so you'll need to switch for every application separately. 4. Start typing. The underlined text is the current composition text. While a composition is active, you can only edit the composition text. Confirm the composition with Space or by selecting a candidate from the candidate list with the arrow keys. Cancel the composition with Escape or by using Backspace to delete the composition text. Note that not all languages offer a candidate list. SDL version-specific issues ------------------------------ - 2.26.5, 2.24.2, 2.0.22: IME candidates work. But there are minor bugs when moving the composition cursor. - 2.0.18, 2.0.20: IME candidates work. - 2.0.16 (our current version): IME candidates cannot be determined with Windows API. Windows tries to draw the composition window like before, so this does not work in fullscreen mode. - 2.0.8 (upstream 0.7): IME candidates work. But this SDL version is too old for us.
2023-01-03 21:28:38 +00:00
m_Input.SetScrollOffset(ScrollOffset);
m_Input.SetScrollOffsetChange(ScrollOffsetChange);
}
#if defined(CONF_VIDEORECORDER)
if(!((g_Config.m_ClShowChat && !IVideo::Current()) || (g_Config.m_ClVideoShowChat && IVideo::Current())))
#else
2018-03-13 20:50:49 +00:00
if(!g_Config.m_ClShowChat)
#endif
return;
y -= 8.0f;
2018-03-13 20:50:49 +00:00
OnPrepareLines();
float ScreenRatio = Graphics()->ScreenAspect();
2021-07-12 09:43:56 +00:00
bool IsScoreBoardOpen = m_pClient->m_Scoreboard.Active() && (ScreenRatio > 1.7f); // only assume scoreboard when screen ratio is widescreen(something around 16:9)
2021-06-23 05:05:49 +00:00
int64_t Now = time();
float HeightLimit = IsScoreBoardOpen ? 180.0f : m_PrevShowChat ? 50.0f : 200.0f;
int OffsetType = IsScoreBoardOpen ? 1 : 0;
2020-10-13 20:08:52 +00:00
float RealMsgPaddingX = MESSAGE_PADDING_X;
float RealMsgPaddingY = MESSAGE_PADDING_Y;
if(g_Config.m_ClChatOld)
2020-10-13 20:08:52 +00:00
{
RealMsgPaddingX = 0;
RealMsgPaddingY = 0;
2020-10-13 20:08:52 +00:00
}
for(int i = 0; i < MAX_LINES; i++)
{
2020-09-22 16:02:03 +00:00
int r = ((m_CurrentLine - i) + MAX_LINES) % MAX_LINES;
if(Now > m_aLines[r].m_Time + 16 * time_freq() && !m_PrevShowChat)
break;
y -= m_aLines[r].m_aYOffset[OffsetType];
2008-09-07 08:44:30 +00:00
// cut off if msgs waste too much space
if(y < HeightLimit)
2008-09-07 08:44:30 +00:00
break;
2020-09-22 16:02:03 +00:00
float Blend = Now > m_aLines[r].m_Time + 14 * time_freq() && !m_PrevShowChat ? 1.0f - (Now - m_aLines[r].m_Time - 14 * time_freq()) / (2.0f * time_freq()) : 1.0f;
2020-10-13 20:08:52 +00:00
// Draw backgrounds for messages in one batch
if(!g_Config.m_ClChatOld)
2020-10-13 20:08:52 +00:00
{
Graphics()->TextureClear();
if(m_aLines[r].m_QuadContainerIndex != -1)
{
Graphics()->SetColor(0, 0, 0, 0.12f * Blend);
Graphics()->RenderQuadContainerEx(m_aLines[r].m_QuadContainerIndex, 0, -1, 0, ((y + RealMsgPaddingY / 2.0f) - m_aLines[r].m_TextYOffset));
}
}
2018-03-13 20:50:49 +00:00
if(m_aLines[r].m_TextContainerIndex != -1)
{
if(!g_Config.m_ClChatOld && m_aLines[r].m_HasRenderTee)
2020-10-13 20:08:52 +00:00
{
CTeeRenderInfo RenderInfo;
RenderInfo.m_CustomColoredSkin = m_aLines[r].m_CustomColoredSkin;
if(m_aLines[r].m_CustomColoredSkin)
RenderInfo.m_ColorableRenderSkin = m_aLines[r].m_RenderSkin;
else
RenderInfo.m_OriginalRenderSkin = m_aLines[r].m_RenderSkin;
2020-11-08 05:39:16 +00:00
RenderInfo.m_SkinMetrics = m_aLines[r].m_RenderSkinMetrics;
2020-10-13 20:08:52 +00:00
RenderInfo.m_ColorBody = m_aLines[r].m_ColorBody;
RenderInfo.m_ColorFeet = m_aLines[r].m_ColorFeet;
RenderInfo.m_Size = MESSAGE_TEE_SIZE;
float RowHeight = FONT_SIZE + RealMsgPaddingY;
float OffsetTeeY = MESSAGE_TEE_SIZE / 2.0f;
float FullHeightMinusTee = RowHeight - MESSAGE_TEE_SIZE;
CAnimState *pIdleState = CAnimState::GetIdle();
2020-11-08 05:39:16 +00:00
vec2 OffsetToMid;
RenderTools()->GetRenderTeeOffsetToRenderedTee(pIdleState, &RenderInfo, OffsetToMid);
vec2 TeeRenderPos(x + (RealMsgPaddingX + MESSAGE_TEE_SIZE) / 2.0f, y + OffsetTeeY + FullHeightMinusTee / 2.0f + OffsetToMid.y);
RenderTools()->RenderTee(pIdleState, &RenderInfo, EMOTE_NORMAL, vec2(1, 0.1f), TeeRenderPos, Blend);
2020-10-13 20:08:52 +00:00
}
ColorRGBA TextOutline(0.f, 0.f, 0.f, 0.3f * Blend);
ColorRGBA Text(1.f, 1.f, 1.f, Blend);
TextRender()->RenderTextContainer(m_aLines[r].m_TextContainerIndex, Text, TextOutline, 0, (y + RealMsgPaddingY / 2.0f) - m_aLines[r].m_TextYOffset);
}
}
}
2010-05-29 07:25:38 +00:00
void CChat::Say(int Team, const char *pLine)
{
m_LastChatSend = time();
2012-01-09 22:13:51 +00:00
// send chat message
2010-05-29 07:25:38 +00:00
CNetMsg_Cl_Say Msg;
Msg.m_Team = Team;
Msg.m_pMessage = pLine;
Client()->SendPackMsgActive(&Msg, MSGFLAG_VITAL);
}
void CChat::SayChat(const char *pLine)
{
if(!pLine || str_length(pLine) < 1)
return;
bool AddEntry = false;
2020-09-22 16:02:03 +00:00
if(m_LastChatSend + time_freq() < time())
{
Say(m_Mode == MODE_ALL ? 0 : 1, pLine);
AddEntry = true;
}
else if(m_PendingChatCounter < 3)
{
++m_PendingChatCounter;
AddEntry = true;
}
if(AddEntry)
{
2020-09-22 16:02:03 +00:00
CHistoryEntry *pEntry = m_History.Allocate(sizeof(CHistoryEntry) + str_length(pLine) - 1);
pEntry->m_Team = m_Mode == MODE_ALL ? 0 : 1;
mem_copy(pEntry->m_aText, pLine, str_length(pLine));
}
}