mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
1292 lines
44 KiB
C++
1292 lines
44 KiB
C++
/******************************************************************************
|
|
-------------------------------------------------------------------------------
|
|
File : IConsole.cpp
|
|
Date : 15:06:2021
|
|
Author : Kawe Mazidjatari
|
|
Purpose: Implements the in-game console front-end
|
|
-------------------------------------------------------------------------------
|
|
History:
|
|
- 15:06:2021 | 14:56 : Created by Kawe Mazidjatari
|
|
- 07:08:2021 | 15:22 : Multi-thread 'CommandExecute' operations to prevent deadlock in render thread
|
|
- 07:08:2021 | 15:25 : Fix a race condition that occurred when detaching the 'CommandExecute' thread
|
|
|
|
******************************************************************************/
|
|
|
|
#include "core/stdafx.h"
|
|
#include "core/init.h"
|
|
#include "core/resource.h"
|
|
#include "tier0/frametask.h"
|
|
#include "tier0/commandline.h"
|
|
#include "windows/id3dx.h"
|
|
#include "windows/console.h"
|
|
#include "windows/resource.h"
|
|
#include "engine/cmd.h"
|
|
#include "gameui/IConsole.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Console variables
|
|
//-----------------------------------------------------------------------------
|
|
static ConVar con_max_lines("con_max_lines", "1024", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Maximum number of lines in the console before cleanup starts", true, 1.f, false, 0.f);
|
|
static ConVar con_max_history("con_max_history", "512", FCVAR_DEVELOPMENTONLY, "Maximum number of command submission items before history cleanup starts", true, 0.f, false, 0.f);
|
|
|
|
static ConVar con_suggest_limit("con_suggest_limit", "128", FCVAR_DEVELOPMENTONLY, "Maximum number of suggestions the autocomplete window will show for the console", true, 0.f, false, 0.f);
|
|
static ConVar con_suggest_helptext("con_suggest_helptext", "1", FCVAR_RELEASE, "Show CommandBase help text in autocomplete window");
|
|
|
|
static ConVar con_autocomplete_window_textures("con_autocomplete_window_textures", "1", FCVAR_RELEASE, "Show help textures in autocomplete window");
|
|
static ConVar con_autocomplete_window_width("con_autocomplete_window_width", "0", FCVAR_RELEASE, "The maximum width of the console's autocomplete window", true, 0.f, false, 0.f);
|
|
static ConVar con_autocomplete_window_height("con_autocomplete_window_height", "217.5", FCVAR_RELEASE, "The maximum height of the console's autocomplete window", true, 0.f, false, 0.f);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Console commands
|
|
//-----------------------------------------------------------------------------
|
|
static ConCommand toggleconsole("toggleconsole", CConsole::ToggleConsole_f, "Show/hide the developer console.", FCVAR_CLIENTDLL | FCVAR_RELEASE);
|
|
|
|
static ConCommand con_history("con_history", CConsole::LogHistory_f, "Shows the developer console submission history", FCVAR_CLIENTDLL | FCVAR_RELEASE);
|
|
static ConCommand con_removeline("con_removeline", CConsole::RemoveLine_f, "Removes a range of lines from the developer console", FCVAR_CLIENTDLL | FCVAR_RELEASE);
|
|
static ConCommand con_clearlines("con_clearlines", CConsole::ClearLines_f, "Clears all lines from the developer console", FCVAR_CLIENTDLL | FCVAR_RELEASE);
|
|
static ConCommand con_clearhistory("con_clearhistory", CConsole::ClearHistory_f, "Clears all submissions from the developer console history", FCVAR_CLIENTDLL | FCVAR_RELEASE);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CConsole::CConsole(void)
|
|
: m_loggerLabel("LoggingRegion")
|
|
, m_historyPos(ConAutoCompletePos_e::kPark)
|
|
, m_suggestPos(ConAutoCompletePos_e::kPark)
|
|
, m_scrollBackAmount(0)
|
|
, m_selectBackAmount(0)
|
|
, m_selectedSuggestionTextLen(0)
|
|
, m_lastFrameScrollPos(0.f, 0.f)
|
|
, m_inputTextBufModified(false)
|
|
, m_canAutoComplete(false)
|
|
, m_autoCompleteActive(false)
|
|
, m_autoCompletePosMoved(false)
|
|
{
|
|
m_surfaceLabel = "Console";
|
|
|
|
memset(m_inputTextBuf, '\0', sizeof(m_inputTextBuf));
|
|
snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "%zu history items", m_vecHistory.size());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CConsole::~CConsole(void)
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: game console initialization
|
|
// Output : true on success, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
bool CConsole::Init(void)
|
|
{
|
|
SetStyleVar(1200, 524, -1000, 50);
|
|
return LoadFlagIcons();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: game console shutdown
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::Shutdown(void)
|
|
{
|
|
for (MODULERESOURCE& flagIcon : m_vecFlagIcons)
|
|
{
|
|
if (flagIcon.m_idIcon)
|
|
{
|
|
flagIcon.m_idIcon->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: game console main render loop
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::RunFrame(void)
|
|
{
|
|
// Uncomment these when adjusting the theme or layout.
|
|
{
|
|
//ImGui::ShowStyleEditor();
|
|
//ImGui::ShowDemoWindow();
|
|
}
|
|
|
|
/**************************
|
|
* BASE PANEL SETUP *
|
|
**************************/
|
|
if (!m_initialized)
|
|
{
|
|
Init();
|
|
m_initialized = true;
|
|
}
|
|
|
|
Animate();
|
|
|
|
int baseWindowStyleVars = 0;
|
|
ImVec2 minBaseWindowRect;
|
|
|
|
if (m_surfaceStyle == ImGuiStyle_t::MODERN)
|
|
{
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); baseWindowStyleVars++;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++;
|
|
|
|
minBaseWindowRect = ImVec2(621.f, 532.f);
|
|
}
|
|
else
|
|
{
|
|
minBaseWindowRect = m_surfaceStyle == ImGuiStyle_t::LEGACY
|
|
? ImVec2(619.f, 526.f)
|
|
: ImVec2(618.f, 524.f);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); baseWindowStyleVars++;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++;
|
|
}
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, minBaseWindowRect); baseWindowStyleVars++;
|
|
|
|
const bool drawn = DrawSurface();
|
|
ImGui::PopStyleVar(baseWindowStyleVars);
|
|
|
|
// If we didn't draw the console, don't draw the suggest panel
|
|
if (!drawn)
|
|
return;
|
|
|
|
/**************************
|
|
* SUGGESTION PANEL SETUP *
|
|
**************************/
|
|
if (RunAutoComplete())
|
|
{
|
|
if (m_surfaceStyle == ImGuiStyle_t::MODERN)
|
|
{
|
|
const ImGuiStyle& style = ImGui::GetStyle();
|
|
m_autoCompleteWindowPos.y = m_autoCompleteWindowPos.y + style.WindowPadding.y + 1.5f;
|
|
}
|
|
|
|
ImGui::SetNextWindowPos(m_autoCompleteWindowPos);
|
|
ImGui::SetNextWindowSize(m_autoCompleteWindowRect);
|
|
|
|
int autoCompleteStyleVars = 0;
|
|
|
|
// NOTE: 68 is the minimum width of the autocomplete window as this
|
|
// leaves enough space to show the flag and the first 4 characters
|
|
// of the suggestion. 37 is the minimum height as anything lower
|
|
// will truncate the first element in the autocomplete window.
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(68, 37)); autoCompleteStyleVars++;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); autoCompleteStyleVars++;
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); autoCompleteStyleVars++;
|
|
|
|
DrawAutoCompletePanel();
|
|
|
|
ImGui::PopStyleVar(autoCompleteStyleVars);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: draws the console's main surface
|
|
// Output : true if a frame has been drawn, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
bool CConsole::DrawSurface(void)
|
|
{
|
|
if (!ImGui::Begin(m_surfaceLabel, &m_activated, ImGuiWindowFlags_None, &ResetInput))
|
|
{
|
|
ImGui::End();
|
|
return false;
|
|
}
|
|
|
|
m_mainWindow = ImGui::GetCurrentWindow();
|
|
|
|
const ImGuiStyle& style = ImGui::GetStyle();
|
|
const ImVec2 fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr);
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
ImGui::Separator();
|
|
if (ImGui::BeginPopup("Options"))
|
|
{
|
|
DrawOptionsPanel();
|
|
}
|
|
if (ImGui::Button("Options"))
|
|
{
|
|
ImGui::OpenPopup("Options");
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
// Reserve enough left-over height and width for 1 separator + 1 input text
|
|
const float footerWidthReserve = style.ItemSpacing.y + ImGui::GetWindowWidth();
|
|
m_colorTextLogger.GetFilter().Draw("Filter (inc,-exc)", footerWidthReserve - 350);
|
|
|
|
ImGui::Separator();
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
if (!m_colorTextLogger.IsScrolledToBottom() && m_scrollBackAmount > 0)
|
|
{
|
|
const ImGuiID windowId = m_mainWindow->GetID(m_loggerLabel);
|
|
|
|
char windowName[128];
|
|
snprintf(windowName, sizeof(windowName), "%s/%s_%08X", m_surfaceLabel, m_loggerLabel, windowId);
|
|
|
|
ImGui::SetWindowScrollY(windowName, m_lastFrameScrollPos.y - m_scrollBackAmount * fontSize.y);
|
|
}
|
|
m_scrollBackAmount = 0;
|
|
|
|
// Reserve enough left-over height for 2 text elements.
|
|
float footerHeightReserve = ImGui::GetFrameHeight() * 2;
|
|
ImGuiChildFlags loggerFlags = ImGuiChildFlags_None;
|
|
|
|
const bool isLegacyStyle = m_surfaceStyle == ImGuiStyle_t::LEGACY;
|
|
int numLoggerStyleVars = 0;
|
|
|
|
if (isLegacyStyle)
|
|
{
|
|
loggerFlags |= ImGuiChildFlags_Border;
|
|
|
|
// Eliminate padding around logger child. This padding gets added when
|
|
// ImGuiChildFlags_Border flag gets set.
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 1.f }); numLoggerStyleVars++;
|
|
|
|
// if we use the legacy theme, also account for one extra space as the
|
|
// legacy theme has an extra separator at the bottom of the logger.
|
|
footerHeightReserve += style.ItemSpacing.y;
|
|
}
|
|
|
|
const static int colorLoggerWindowFlags =
|
|
ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_HorizontalScrollbar |
|
|
ImGuiWindowFlags_NoNavInputs |
|
|
ImGuiWindowFlags_OverlayHorizontalScrollbar;
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); numLoggerStyleVars++;
|
|
ImGui::BeginChild(m_loggerLabel, ImVec2(0, -footerHeightReserve), loggerFlags, colorLoggerWindowFlags);
|
|
|
|
// NOTE: scoped so the mutex releases after we have rendered.
|
|
// this is currently the only place the color logger is used
|
|
// during the drawing of the base panel.
|
|
{
|
|
AUTO_LOCK(m_colorTextLoggerMutex);
|
|
m_colorTextLogger.Render();
|
|
}
|
|
|
|
m_lastFrameScrollPos = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());
|
|
|
|
ImGui::EndChild();
|
|
|
|
if (numLoggerStyleVars)
|
|
ImGui::PopStyleVar(numLoggerStyleVars);
|
|
|
|
// The legacy theme also has a spacer here.
|
|
if (isLegacyStyle)
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("%s", m_summaryTextBuf);
|
|
|
|
const std::function<void(void)> fnHandleInput = [&](void)
|
|
{
|
|
if (m_inputTextBuf[0])
|
|
{
|
|
ProcessCommand(m_inputTextBuf);
|
|
ResetAutoCompleteData();
|
|
|
|
m_inputTextBufModified = true;
|
|
}
|
|
|
|
BuildSummaryText("");
|
|
m_reclaimFocus = true;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
const static int inputTextFieldFlags =
|
|
ImGuiInputTextFlags_EnterReturnsTrue |
|
|
ImGuiInputTextFlags_CallbackCompletion |
|
|
ImGuiInputTextFlags_CallbackHistory |
|
|
ImGuiInputTextFlags_CallbackAlways |
|
|
ImGuiInputTextFlags_CallbackCharFilter |
|
|
ImGuiInputTextFlags_CallbackEdit |
|
|
ImGuiInputTextFlags_AutoCaretEnd;
|
|
|
|
ImGui::PushItemWidth(footerWidthReserve - 80);
|
|
if (ImGui::InputText("##input", m_inputTextBuf, IM_ARRAYSIZE(m_inputTextBuf), inputTextFieldFlags, &TextEditCallbackStub, reinterpret_cast<void*>(this)))
|
|
{
|
|
// If we selected something in the suggestions window, create the
|
|
// command from that instead
|
|
if (m_suggestPos > ConAutoCompletePos_e::kPark)
|
|
{
|
|
DetermineInputTextFromSelectedSuggestion(m_vecSuggest[m_suggestPos], m_selectedSuggestionText);
|
|
BuildSummaryText(m_selectedSuggestionText.c_str());
|
|
|
|
m_inputTextBufModified = true;
|
|
m_reclaimFocus = true;
|
|
}
|
|
else
|
|
{
|
|
fnHandleInput();
|
|
}
|
|
}
|
|
|
|
// Auto-focus input field on window apparition.
|
|
ImGui::SetItemDefaultFocus();
|
|
|
|
// Auto-focus input field if reclaim is demanded.
|
|
if (m_reclaimFocus)
|
|
{
|
|
ImGui::SetKeyboardFocusHere(-1); // -1 means previous widget.
|
|
m_reclaimFocus = false;
|
|
}
|
|
|
|
DetermineAutoCompleteWindowRect();
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Submit"))
|
|
{
|
|
fnHandleInput();
|
|
}
|
|
|
|
ImGui::End();
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: draws the options panel
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::DrawOptionsPanel(void)
|
|
{
|
|
ImGui::Checkbox("Auto-scroll", &m_colorTextLogger.m_bAutoScroll);
|
|
|
|
ImGui::SameLine();
|
|
ImGui::PushItemWidth(100);
|
|
|
|
ImGui::PopItemWidth();
|
|
|
|
if (ImGui::SmallButton("Clear"))
|
|
{
|
|
ClearLog();
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
// Copies all logged text to the clip board
|
|
if (ImGui::SmallButton("Copy"))
|
|
{
|
|
AUTO_LOCK(m_colorTextLoggerMutex);
|
|
m_colorTextLogger.Copy(true);
|
|
}
|
|
|
|
ImGui::Text("Console hotkey:");
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Hotkey("##ToggleConsole", &g_ImGuiConfig.m_ConsoleConfig.m_nBind0, ImVec2(80, 80)))
|
|
{
|
|
g_ImGuiConfig.Save();
|
|
}
|
|
|
|
ImGui::Text("Browser hotkey:");
|
|
ImGui::SameLine();
|
|
|
|
if (ImGui::Hotkey("##ToggleBrowser", &g_ImGuiConfig.m_BrowserConfig.m_nBind0, ImVec2(80, 80)))
|
|
{
|
|
g_ImGuiConfig.Save();
|
|
}
|
|
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: draws the autocomplete panel with results based on user input
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::DrawAutoCompletePanel(void)
|
|
{
|
|
const static int autoCompleteWindowFlags =
|
|
ImGuiWindowFlags_NoTitleBar |
|
|
ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoSavedSettings |
|
|
ImGuiWindowFlags_NoFocusOnAppearing |
|
|
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
|
ImGuiWindowFlags_AlwaysHorizontalScrollbar;
|
|
|
|
ImGui::Begin("##suggest", nullptr, autoCompleteWindowFlags);
|
|
ImGui::PushAllowKeyboardFocus(false);
|
|
|
|
ImGuiWindow* const autocompleteWindow = ImGui::GetCurrentWindow();
|
|
|
|
// NOTE: this makes sure we always draw this window behind the main console
|
|
// window, this is necessary as otherwise if you were to drag another
|
|
// window above the console, and then focus on the console again, that
|
|
// window will now be in between the console window and the autocomplete
|
|
// suggest window.
|
|
ImGui::BringWindowToDisplayBehind(autocompleteWindow, m_mainWindow);
|
|
|
|
for (size_t i = 0, ns = m_vecSuggest.size(); i < ns; i++)
|
|
{
|
|
const ConAutoCompleteSuggest_s& suggest = m_vecSuggest[i];
|
|
const bool isIndexActive = m_suggestPos == ssize_t(i);
|
|
|
|
ImGui::PushID(static_cast<int>(i));
|
|
|
|
if (m_autoCompleteTexturesLoaded && con_autocomplete_window_textures.GetBool())
|
|
{
|
|
// Show the flag texture before the cvar name.
|
|
const int mainTexIdx = GetFlagTextureIndex(suggest.flags);
|
|
const MODULERESOURCE& mainRes = m_vecFlagIcons[mainTexIdx];
|
|
|
|
ImGui::Image(mainRes.m_idIcon, ImVec2(float(mainRes.m_nWidth), float(mainRes.m_nHeight)));
|
|
|
|
// Show a more detailed description of the flag when user hovers over the texture.
|
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly) &&
|
|
suggest.flags != COMMAND_COMPLETION_MARKER)
|
|
{
|
|
const std::function<void(const ConVarFlags::FlagDesc_t&)> fnAddHint = [&](const ConVarFlags::FlagDesc_t& cvarInfo)
|
|
{
|
|
const int hintTexIdx = GetFlagTextureIndex(cvarInfo.bit);
|
|
const MODULERESOURCE& hintRes = m_vecFlagIcons[hintTexIdx];
|
|
|
|
ImGui::Image(hintRes.m_idIcon, ImVec2(float(hintRes.m_nWidth), float(hintRes.m_nHeight)));
|
|
ImGui::SameLine();
|
|
ImGui::Text("%s", cvarInfo.shortdesc);
|
|
};
|
|
|
|
ImGui::BeginTooltip();
|
|
bool isFlagSet = false;
|
|
|
|
// Reverse loop to display the most significant flag first.
|
|
for (int j = IM_ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); (j--) > 0;)
|
|
{
|
|
const ConVarFlags::FlagDesc_t& info = g_ConVarFlags.m_FlagsToDesc[j];
|
|
if (suggest.flags & info.bit)
|
|
{
|
|
isFlagSet = true;
|
|
fnAddHint(info);
|
|
}
|
|
}
|
|
if (!isFlagSet) // Display the FCVAR_NONE flag if no flags are set.
|
|
{
|
|
fnAddHint(g_ConVarFlags.m_FlagsToDesc[0]);
|
|
}
|
|
|
|
ImGui::EndTooltip();
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
}
|
|
|
|
if (ImGui::Selectable(suggest.text.c_str(), isIndexActive))
|
|
{
|
|
ImGui::Separator();
|
|
string newInputText;
|
|
|
|
DetermineInputTextFromSelectedSuggestion(suggest, newInputText);
|
|
memmove(m_inputTextBuf, newInputText.data(), newInputText.size() + 1);
|
|
|
|
m_canAutoComplete = true;
|
|
m_reclaimFocus = true;
|
|
|
|
BuildSummaryText(newInputText.c_str());
|
|
}
|
|
|
|
ImGui::PopID();
|
|
|
|
// Update the suggest position
|
|
if (m_autoCompletePosMoved)
|
|
{
|
|
if (isIndexActive) // Bring the 'active' element into view
|
|
{
|
|
ImRect imRect = ImGui::GetCurrentContext()->LastItemData.Rect;
|
|
|
|
// Reset to keep flag icon in display.
|
|
imRect.Min.x = autocompleteWindow->InnerRect.Min.x;
|
|
imRect.Max.x = autocompleteWindow->InnerRect.Max.x;
|
|
|
|
// Eliminate jiggle when going up/down in the menu.
|
|
imRect.Min.y += 1;
|
|
imRect.Max.y -= 1;
|
|
|
|
ImGui::ScrollToRect(autocompleteWindow, imRect);
|
|
m_autoCompletePosMoved = false;
|
|
}
|
|
else if (m_suggestPos == ConAutoCompletePos_e::kPark)
|
|
{
|
|
// Reset position; kPark = no active element.
|
|
ImGui::SetScrollX(0.0f);
|
|
ImGui::SetScrollY(0.0f);
|
|
|
|
m_autoCompletePosMoved = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::PopAllowKeyboardFocus();
|
|
ImGui::End();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: runs the auto complete for the console
|
|
// Output : true if auto complete is performed, false otherwise
|
|
//-----------------------------------------------------------------------------
|
|
bool CConsole::RunAutoComplete(void)
|
|
{
|
|
// Don't suggest if user tries to assign value to ConVar or execute ConCommand.
|
|
if (!m_inputTextBuf[0] || strstr(m_inputTextBuf, ";"))
|
|
{
|
|
if (m_autoCompleteActive)
|
|
{
|
|
ResetAutoCompleteData();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!strstr(m_inputTextBuf, " "))
|
|
{
|
|
if (m_canAutoComplete)
|
|
{
|
|
CreateSuggestionsFromPartial();
|
|
}
|
|
}
|
|
else if (m_canAutoComplete) // Command completion callback.
|
|
{
|
|
ResetAutoCompleteData();
|
|
|
|
char szCommand[sizeof(m_inputTextBuf)];
|
|
size_t i = 0;
|
|
|
|
// Truncate everything past (and including) the space to get the
|
|
// command string.
|
|
for (; i < sizeof(m_inputTextBuf); i++)
|
|
{
|
|
const char c = m_inputTextBuf[i];
|
|
|
|
if (c == '\0' || isspace(c))
|
|
{
|
|
break;
|
|
}
|
|
|
|
szCommand[i] = c;
|
|
}
|
|
|
|
szCommand[i] = '\0';
|
|
ConCommand* const pCommand = g_pCVar->FindCommand(szCommand);
|
|
|
|
if (pCommand && pCommand->CanAutoComplete())
|
|
{
|
|
CUtlVector< CUtlString > commands;
|
|
const int iret = pCommand->AutoCompleteSuggest(m_inputTextBuf, commands);
|
|
|
|
if (!iret)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int j = 0; j < iret; ++j)
|
|
{
|
|
m_vecSuggest.push_back(ConAutoCompleteSuggest_s(commands[j].String(), COMMAND_COMPLETION_MARKER));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (m_vecSuggest.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_autoCompleteActive = true;
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: resets the auto complete window
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ResetAutoCompleteData(void)
|
|
{
|
|
m_suggestPos = ConAutoCompletePos_e::kPark;
|
|
m_canAutoComplete = false;
|
|
m_autoCompleteActive = false;
|
|
m_autoCompletePosMoved = true;
|
|
m_vecSuggest.clear();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: find ConVars/ConCommands from user input and add to vector
|
|
// - Ignores ConVars marked FCVAR_HIDDEN
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::CreateSuggestionsFromPartial(void)
|
|
{
|
|
ResetAutoCompleteData();
|
|
|
|
ICvar::Iterator iter(g_pCVar);
|
|
for (iter.SetFirst(); iter.IsValid(); iter.Next())
|
|
{
|
|
if (m_vecSuggest.size() >= con_suggest_limit.GetInt())
|
|
{
|
|
break;
|
|
}
|
|
|
|
const ConCommandBase* const commandBase = iter.Get();
|
|
|
|
if (commandBase->IsFlagSet(FCVAR_HIDDEN))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const char* const commandName = commandBase->GetName();
|
|
|
|
if (!V_stristr(commandName, m_inputTextBuf))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (std::find(m_vecSuggest.begin(), m_vecSuggest.end(),
|
|
commandName) == m_vecSuggest.end())
|
|
{
|
|
string docString;
|
|
|
|
if (!commandBase->IsCommand())
|
|
{
|
|
const ConVar* conVar = reinterpret_cast<const ConVar*>(commandBase);
|
|
|
|
docString = " = ["; // Assign current value to string if its a ConVar.
|
|
docString.append(conVar->GetString());
|
|
docString.append("]");
|
|
}
|
|
if (con_suggest_helptext.GetBool())
|
|
{
|
|
std::function<void(string& , const char*)> fnAppendDocString = [&](string& targetString, const char* toAppend)
|
|
{
|
|
if (VALID_CHARSTAR(toAppend))
|
|
{
|
|
targetString.append(" - \"");
|
|
targetString.append(toAppend);
|
|
targetString.append("\"");
|
|
}
|
|
};
|
|
|
|
fnAppendDocString(docString, commandBase->GetHelpText());
|
|
fnAppendDocString(docString, commandBase->GetUsageText());
|
|
}
|
|
m_vecSuggest.push_back(ConAutoCompleteSuggest_s(commandName + docString, commandBase->GetFlags()));
|
|
}
|
|
else
|
|
{
|
|
// Trying to push a duplicate ConCommandBase in the vector; code bug.
|
|
Assert(0);
|
|
}
|
|
}
|
|
|
|
std::sort(m_vecSuggest.begin(), m_vecSuggest.end());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: processes submitted commands for the main thread
|
|
// Input : inputText -
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ProcessCommand(const char* const inputText)
|
|
{
|
|
string commandFormatted(inputText);
|
|
StringRTrim(commandFormatted, " "); // Remove trailing white space characters to prevent history duplication.
|
|
|
|
const ImU32 commandColor = ImGui::ColorConvertFloat4ToU32(ImVec4(1.00f, 0.80f, 0.60f, 1.00f));
|
|
AddLog(commandColor, "%s] %s\n", Plat_GetProcessUpTime(), commandFormatted.c_str());
|
|
|
|
Cbuf_AddText(Cbuf_GetCurrentPlayer(), commandFormatted.c_str(), cmd_source_t::kCommandSrcCode);
|
|
m_historyPos = ConAutoCompletePos_e::kPark;
|
|
|
|
AddHistory(commandFormatted.c_str());
|
|
|
|
m_colorTextLogger.ShouldScrollToStart(true);
|
|
m_colorTextLogger.ShouldScrollToBottom(true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: builds the console summary, this function will attempt to search
|
|
// for a ConVar first, from which it formats the current and default
|
|
// value. If the string is empty, or no ConVar is found, the function
|
|
// formats the number of history items instead
|
|
// Input : inputText -
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::BuildSummaryText(const char* const inputText)
|
|
{
|
|
if (*inputText)
|
|
{
|
|
string conVarFormatted(inputText);
|
|
|
|
// Remove trailing space and/or semicolon before we call 'g_pCVar->FindVar(..)'.
|
|
StringRTrim(conVarFormatted, " ;", true);
|
|
const ConVar* const conVar = g_pCVar->FindVar(conVarFormatted.c_str());
|
|
|
|
if (conVar && !conVar->IsFlagSet(FCVAR_HIDDEN))
|
|
{
|
|
// Display the current and default value of ConVar if found.
|
|
snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "(\"%s\", default \"%s\")",
|
|
conVar->GetString(), conVar->GetDefault());
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "%zu history items", m_vecHistory.size());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: creates the selected suggestion for input field
|
|
// Input : &suggest -
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::DetermineInputTextFromSelectedSuggestion(const ConAutoCompleteSuggest_s& suggest, string& svInput)
|
|
{
|
|
if (suggest.flags == COMMAND_COMPLETION_MARKER)
|
|
{
|
|
svInput = suggest.text + ' ';
|
|
}
|
|
else // Remove the default value from ConVar before assigning it to the input buffer.
|
|
{
|
|
svInput = suggest.text.substr(0, suggest.text.find(' ')) + ' ';
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: determines the autocomplete window rect
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::DetermineAutoCompleteWindowRect(void)
|
|
{
|
|
float flSinglePadding = 0.f;
|
|
const float flItemHeight = ImGui::GetTextLineHeightWithSpacing() + 1.0f;
|
|
|
|
if (m_vecSuggest.size() > 1)
|
|
{
|
|
// Pad with 18 to keep all items in view.
|
|
flSinglePadding = flItemHeight;
|
|
}
|
|
|
|
// NOTE: last item rect = the input text box, the idea here is to set the
|
|
// pos to that of the input text bar, whilst also clamping the width to it.
|
|
const ImVec2 lastItemRectMin = ImGui::GetItemRectMin();
|
|
const ImVec2 lastItemRectSize = ImGui::GetItemRectSize();
|
|
|
|
m_autoCompleteWindowPos = lastItemRectMin;
|
|
m_autoCompleteWindowPos.y += lastItemRectSize.y;
|
|
|
|
const float maxWindowWidth = con_autocomplete_window_width.GetFloat();
|
|
|
|
const float flWindowWidth = maxWindowWidth > 0
|
|
? ImMin(maxWindowWidth, lastItemRectSize.x)
|
|
: lastItemRectSize.x;
|
|
|
|
// NOTE: minimum vertical size of the window, going below this will
|
|
// truncate the first element in the window making it looked bugged.
|
|
const static float minWindowHeight = 37.0f;
|
|
|
|
const float flWindowHeight = flSinglePadding + ImClamp(
|
|
static_cast<float>(m_vecSuggest.size() * flItemHeight),
|
|
minWindowHeight,
|
|
con_autocomplete_window_height.GetFloat());
|
|
|
|
m_autoCompleteWindowRect = ImVec2(flWindowWidth, flWindowHeight);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: loads flag images from resource section (must be aligned with resource.h!)
|
|
// Output : true on success, false on failure
|
|
//-----------------------------------------------------------------------------
|
|
bool CConsole::LoadFlagIcons(void)
|
|
{
|
|
bool ret = false;
|
|
|
|
// Get all flag image resources for displaying flags.
|
|
for (int i = IDB_PNG3, k = NULL; i <= IDB_PNG32; i++, k++)
|
|
{
|
|
m_vecFlagIcons.push_back(MODULERESOURCE(GetModuleResource(i)));
|
|
MODULERESOURCE& rFlagIcon = m_vecFlagIcons[k];
|
|
|
|
ret = LoadTextureBuffer(reinterpret_cast<unsigned char*>(rFlagIcon.m_pData), // !TODO: Fall-back texture.
|
|
static_cast<int>(rFlagIcon.m_nSize), &rFlagIcon.m_idIcon, &rFlagIcon.m_nWidth, &rFlagIcon.m_nHeight);
|
|
|
|
if (!ret)
|
|
{
|
|
Assert(0, "Texture flags load failed for %i", i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_autoCompleteTexturesLoaded = ret;
|
|
return ret;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns flag texture index for CommandBase (must be aligned with resource.h!)
|
|
// in the future we should build the texture procedurally with use of popcnt.
|
|
// Input : nFlags -
|
|
//-----------------------------------------------------------------------------
|
|
int CConsole::GetFlagTextureIndex(const int flags) const
|
|
{
|
|
switch (flags) // All indices for single/dual flag textures.
|
|
{
|
|
case FCVAR_DEVELOPMENTONLY:
|
|
return 9;
|
|
case FCVAR_GAMEDLL:
|
|
return 10;
|
|
case FCVAR_CLIENTDLL:
|
|
return 11;
|
|
case FCVAR_REPLICATED:
|
|
return 12;
|
|
case FCVAR_CHEAT:
|
|
return 13;
|
|
case FCVAR_RELEASE:
|
|
return 14;
|
|
case FCVAR_MATERIAL_SYSTEM_THREAD:
|
|
return 15;
|
|
case FCVAR_DEVELOPMENTONLY | FCVAR_GAMEDLL:
|
|
return 16;
|
|
case FCVAR_DEVELOPMENTONLY | FCVAR_CLIENTDLL:
|
|
return 17;
|
|
case FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED:
|
|
return 18;
|
|
case FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT:
|
|
return 19;
|
|
case FCVAR_DEVELOPMENTONLY | FCVAR_MATERIAL_SYSTEM_THREAD:
|
|
return 20;
|
|
case FCVAR_REPLICATED | FCVAR_CHEAT:
|
|
return 21;
|
|
case FCVAR_REPLICATED | FCVAR_RELEASE:
|
|
return 22;
|
|
case FCVAR_GAMEDLL | FCVAR_CHEAT:
|
|
return 23;
|
|
case FCVAR_GAMEDLL | FCVAR_RELEASE:
|
|
return 24;
|
|
case FCVAR_CLIENTDLL | FCVAR_CHEAT:
|
|
return 25;
|
|
case FCVAR_CLIENTDLL | FCVAR_RELEASE:
|
|
return 26;
|
|
case FCVAR_MATERIAL_SYSTEM_THREAD | FCVAR_CHEAT:
|
|
return 27;
|
|
case FCVAR_MATERIAL_SYSTEM_THREAD | FCVAR_RELEASE:
|
|
return 28;
|
|
case COMMAND_COMPLETION_MARKER:
|
|
return 29;
|
|
|
|
default: // Hit when flag is zero/non-indexed or 3+ bits are set.
|
|
|
|
const unsigned int v = __popcnt(flags);
|
|
switch (v)
|
|
{
|
|
case 0:
|
|
return 0; // Pink checker texture (FCVAR_NONE)
|
|
case 1:
|
|
return 1; // Yellow checker texture (non-indexed).
|
|
default:
|
|
|
|
// If 3 or more bits are set, we test the flags
|
|
// and display the appropriate checker texture.
|
|
bool mul = v > 2;
|
|
|
|
if (flags & FCVAR_DEVELOPMENTONLY)
|
|
{
|
|
return mul ? 4 : 3;
|
|
}
|
|
else if (flags & FCVAR_CHEAT)
|
|
{
|
|
return mul ? 6 : 5;
|
|
}
|
|
else if (flags & FCVAR_RELEASE && // RELEASE command but no context restriction.
|
|
!(flags & FCVAR_SERVER_CAN_EXECUTE) &&
|
|
!(flags & FCVAR_CLIENTCMD_CAN_EXECUTE))
|
|
{
|
|
return mul ? 8 : 7;
|
|
}
|
|
|
|
// Rainbow checker texture (user needs to manually check flags).
|
|
// These commands are not restricted if ran from the same context.
|
|
return 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: console input box callback
|
|
// Input : *iData -
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData)
|
|
{
|
|
switch (iData->EventFlag)
|
|
{
|
|
case ImGuiInputTextFlags_CallbackCompletion:
|
|
{
|
|
// Locate beginning of current word.
|
|
const char* pszWordEnd = iData->Buf + iData->CursorPos;
|
|
const char* pszWordStart = pszWordEnd;
|
|
while (pszWordStart > iData->Buf)
|
|
{
|
|
const char c = pszWordStart[-1];
|
|
if (c == ' ' || c == '\t' || c == ',' || c == ';')
|
|
{
|
|
break;
|
|
}
|
|
pszWordStart--;
|
|
}
|
|
break;
|
|
}
|
|
case ImGuiInputTextFlags_CallbackHistory:
|
|
{
|
|
if (m_autoCompleteActive)
|
|
{
|
|
if (iData->EventKey == ImGuiKey_UpArrow && m_suggestPos > - 1)
|
|
{
|
|
m_suggestPos--;
|
|
m_autoCompletePosMoved = true;
|
|
}
|
|
else if (iData->EventKey == ImGuiKey_DownArrow)
|
|
{
|
|
if (m_suggestPos < static_cast<int>(m_vecSuggest.size()) - 1)
|
|
{
|
|
m_suggestPos++;
|
|
m_autoCompletePosMoved = true;
|
|
}
|
|
}
|
|
}
|
|
else // Allow user to navigate through the history if suggest panel isn't drawn.
|
|
{
|
|
const int prevHistoryPos = m_historyPos;
|
|
|
|
if (iData->EventKey == ImGuiKey_UpArrow)
|
|
{
|
|
if (m_historyPos == ConAutoCompletePos_e::kPark)
|
|
{
|
|
m_historyPos = static_cast<int>(m_vecHistory.size()) - 1;
|
|
}
|
|
else if (m_historyPos > 0)
|
|
{
|
|
m_historyPos--;
|
|
}
|
|
}
|
|
else if (iData->EventKey == ImGuiKey_DownArrow)
|
|
{
|
|
if (m_historyPos != ConAutoCompletePos_e::kPark)
|
|
{
|
|
if (++m_historyPos >= static_cast<int>(m_vecHistory.size()))
|
|
{
|
|
m_historyPos = ConAutoCompletePos_e::kPark;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prevHistoryPos != m_historyPos)
|
|
{
|
|
string historyText = (m_historyPos >= 0) ? m_vecHistory[m_historyPos] : "";
|
|
|
|
if (!historyText.empty())
|
|
{
|
|
if (historyText.find(' ') == string::npos)
|
|
{
|
|
// Append whitespace to previous entered command if
|
|
// absent or no parameters where passed. This is to
|
|
// the user could directly start adding their own
|
|
// params without needing to hit the space bar first.
|
|
historyText.append(" ");
|
|
}
|
|
}
|
|
|
|
iData->DeleteChars(0, iData->BufTextLen);
|
|
iData->InsertChars(0, historyText.c_str());
|
|
}
|
|
}
|
|
|
|
BuildSummaryText(iData->Buf);
|
|
break;
|
|
}
|
|
case ImGuiInputTextFlags_CallbackAlways:
|
|
{
|
|
m_selectedSuggestionTextLen = iData->BufTextLen;
|
|
|
|
if (m_inputTextBufModified) // User entered a value in the input field.
|
|
{
|
|
iData->DeleteChars(0, m_selectedSuggestionTextLen);
|
|
|
|
if (!m_selectedSuggestionText.empty()) // User selected a ConVar from the suggestion window, copy it to the buffer.
|
|
{
|
|
iData->InsertChars(0, m_selectedSuggestionText.c_str());
|
|
m_selectedSuggestionText.clear();
|
|
|
|
m_canAutoComplete = true;
|
|
m_reclaimFocus = true;
|
|
}
|
|
|
|
m_inputTextBufModified = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ImGuiInputTextFlags_CallbackCharFilter:
|
|
{
|
|
const ImWchar c = iData->EventChar;
|
|
|
|
if (!m_selectedSuggestionTextLen)
|
|
{
|
|
if (c == '~') // Discard tilde character as first input.
|
|
{
|
|
iData->EventChar = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (c == '`') // Discard back quote character (default console invoke key).
|
|
{
|
|
iData->EventChar = 0;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
case ImGuiInputTextFlags_CallbackEdit:
|
|
{
|
|
// If user selected all text in the input field and replaces it with
|
|
// a tilde or space character, it will be set as the first character
|
|
// in the input field as m_nInputTextLen is set before the actual edit.
|
|
while (iData->Buf[0] == '~' || iData->Buf[0] == ' ')
|
|
{
|
|
iData->DeleteChars(0, 1);
|
|
}
|
|
|
|
if (iData->BufTextLen)
|
|
{
|
|
m_canAutoComplete = true;
|
|
}
|
|
else // Reset state and enable history scrolling when buffer is empty.
|
|
{
|
|
ResetAutoCompleteData();
|
|
}
|
|
|
|
BuildSummaryText(iData->Buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: console input box callback stub
|
|
// Input : *iData -
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
int CConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* iData)
|
|
{
|
|
CConsole* const pConsole = reinterpret_cast<CConsole*>(iData->UserData);
|
|
return pConsole->TextEditCallback(iData);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: adds logs to the console; this is the only place text is added to
|
|
// the vector, do not call 'm_Logger.InsertText' elsewhere as we also manage
|
|
// the size of the vector here !!!
|
|
// Input : &conLog -
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::AddLog(const char* const text, const ImU32 color)
|
|
{
|
|
AUTO_LOCK(m_colorTextLoggerMutex);
|
|
|
|
m_colorTextLogger.InsertText(text, color);
|
|
ClampLogSize();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: adds logs to the console (internal)
|
|
// Input : &color -
|
|
// *fmt -
|
|
// ... -
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::AddLog(const ImU32 color, const char* fmt, ...) /*IM_FMTARGS(2)*/
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
string result = FormatV(fmt, args);
|
|
va_end(args);
|
|
|
|
AddLog(result.c_str(), color);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: removes lines from console with sanitized start and end indices
|
|
// input : nStart -
|
|
// nEnd -
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::RemoveLog(int nStart, int nEnd)
|
|
{
|
|
AUTO_LOCK(m_colorTextLoggerMutex);
|
|
|
|
const int numLines = m_colorTextLogger.GetTotalLines();
|
|
|
|
if (nEnd >= numLines)
|
|
{
|
|
// Sanitize for last array elem.
|
|
nEnd = (numLines - 1);
|
|
}
|
|
|
|
if (nStart >= nEnd)
|
|
{
|
|
if (nEnd > 0)
|
|
{
|
|
nStart = (nEnd - 1);
|
|
}
|
|
else
|
|
{
|
|
// First elem cannot be removed!
|
|
return;
|
|
}
|
|
}
|
|
else if (nStart < 0)
|
|
{
|
|
nStart = 0;
|
|
}
|
|
|
|
// User wants to remove everything.
|
|
if (numLines <= (nStart - nEnd))
|
|
{
|
|
ClearLog();
|
|
return;
|
|
}
|
|
|
|
m_colorTextLogger.RemoveLine(nStart, nEnd);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clears the entire log vector
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ClearLog(void)
|
|
{
|
|
AUTO_LOCK(m_colorTextLoggerMutex);
|
|
m_colorTextLogger.RemoveLine(0, (m_colorTextLogger.GetTotalLines() - 1));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clamps the size of the log vector
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ClampLogSize(void)
|
|
{
|
|
// +1 since the first row is a dummy
|
|
const int maxLines = con_max_lines.GetInt() + 1;
|
|
|
|
if (m_colorTextLogger.GetTotalLines() > maxLines)
|
|
{
|
|
while (m_colorTextLogger.GetTotalLines() > maxLines)
|
|
{
|
|
m_colorTextLogger.RemoveLine(0);
|
|
|
|
m_scrollBackAmount++;
|
|
m_selectBackAmount++;
|
|
}
|
|
|
|
m_colorTextLogger.MoveSelection(m_selectBackAmount, false);
|
|
m_colorTextLogger.MoveCursor(m_selectBackAmount, false);
|
|
|
|
m_selectBackAmount = 0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: adds a command to the history vector; this is the only place text
|
|
// is added to the vector, do not call 'm_History.push_back' elsewhere as we
|
|
// also manage the size of the vector here !!!
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::AddHistory(const char* const command)
|
|
{
|
|
// If this command was already in the history, remove it so when we push it
|
|
// in, it would appear all the way at the top of the list
|
|
for (size_t i = m_vecHistory.size(); i-- > 0;)
|
|
{
|
|
if (m_vecHistory[i].compare(command) == 0)
|
|
{
|
|
m_vecHistory.erase(m_vecHistory.begin() + i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_vecHistory.push_back(command);
|
|
ClampHistorySize();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: gets all console submissions
|
|
// Output : vector of strings
|
|
//-----------------------------------------------------------------------------
|
|
const vector<string>& CConsole::GetHistory(void) const
|
|
{
|
|
return m_vecHistory;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clears the entire submission history vector
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ClearHistory(void)
|
|
{
|
|
m_vecHistory.clear();
|
|
BuildSummaryText("");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clamps the size of the history vector
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ClampHistorySize(void)
|
|
{
|
|
while (m_vecHistory.size() > con_max_history.GetInt())
|
|
{
|
|
m_vecHistory.erase(m_vecHistory.begin());
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: toggles the console
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ToggleConsole_f()
|
|
{
|
|
g_Console.m_activated ^= true;
|
|
ResetInput(); // Disable input to game when console is drawn.
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: shows the game console submission history.
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::LogHistory_f()
|
|
{
|
|
const vector<string>& vHistory = g_Console.GetHistory();
|
|
for (size_t i = 0, nh = vHistory.size(); i < nh; i++)
|
|
{
|
|
Msg(eDLL_T::COMMON, "%3d: %s\n", i, vHistory[i].c_str());
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: removes a range of lines from the console.
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::RemoveLine_f(const CCommand& args)
|
|
{
|
|
if (args.ArgC() < 3)
|
|
{
|
|
Msg(eDLL_T::CLIENT, "Usage 'con_removeline': start(int) end(int)\n");
|
|
return;
|
|
}
|
|
|
|
const int start = atoi(args[1]);
|
|
const int end = atoi(args[2]);
|
|
|
|
g_Console.RemoveLog(start, end);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clears all lines from the developer console.
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ClearLines_f()
|
|
{
|
|
g_Console.ClearLog();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clears all submissions from the developer console history.
|
|
//-----------------------------------------------------------------------------
|
|
void CConsole::ClearHistory_f()
|
|
{
|
|
g_Console.ClearHistory();
|
|
}
|
|
|
|
CConsole g_Console;
|