From c6edc47c18046b2406354344924644a787816009 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:39:06 +0100 Subject: [PATCH] ImGui: console and browser code style refactor Made the style of the code more consistent. Not much logic-wise had changed during the refactor, but there are some. Console: * Removed m_bSuggestUpdate and the logic bound to it, this var was never set and the logic was never used. This was the initial workaround for resetting the autocomplete selection pos to kPark (-1), but this is currently done in a more mature way, but the old code was never removed * CreateSuggestionsFromPartial() (previously, FindFromPartial()) would break if a ConCommand/ConVar was found that was already added in the list. Although this is an engine/sdk level bug, we shouldn't stop iterating there. Added an assert instead and made the loop continue. * Removed member m_bCopyToClipBoard, this was used to check if the copy button was pressed, to copy the text the next frame. The copy now happens directly in the site of the Copy button which is a better approach. * Due to the changes with removing m_bCopyToClipBoard, the mutex for m_colorTextLogger is now released right after rendering the color text to fully minimize blocking time between other threads. Browser: * Moved ImGui::Begin() call from RunFrame() to DrawSurface(), it fits better there and we can now also return false if the frame didn't render. * Improved the "Broadcasting" text formatting when server browser is hosting and broadcasting without a server token (public server). Both: * Added virtual Shutdown() method. --- r5dev/gameui/IBrowser.cpp | 294 +++++++-------- r5dev/gameui/IBrowser.h | 39 +- r5dev/gameui/IConsole.cpp | 671 ++++++++++++++++++----------------- r5dev/gameui/IConsole.h | 137 ++++--- r5dev/gameui/imgui_surface.h | 2 + 5 files changed, 607 insertions(+), 536 deletions(-) diff --git a/r5dev/gameui/IBrowser.cpp b/r5dev/gameui/IBrowser.cpp index f44dffd4..d52fc7a2 100644 --- a/r5dev/gameui/IBrowser.cpp +++ b/r5dev/gameui/IBrowser.cpp @@ -43,18 +43,18 @@ static ConCommand togglebrowser("togglebrowser", CBrowser::ToggleBrowser_f, "Sho // Purpose: //----------------------------------------------------------------------------- CBrowser::CBrowser(void) - : m_bReclaimFocusTokenField(false) - , m_bQueryListNonRecursive(false) - , m_bQueryGlobalBanList(true) - , m_HostMessageColor(1.00f, 1.00f, 1.00f, 1.00f) - , m_ivHiddenServerMessageColor(0.00f, 1.00f, 0.00f, 1.00f) + : m_reclaimFocusOnTokenField(false) + , m_queryNewListNonRecursive(false) + , m_queryGlobalBanList(true) + , m_hostMessageColor(1.00f, 1.00f, 1.00f, 1.00f) + , m_hiddenServerMessageColor(0.00f, 1.00f, 0.00f, 1.00f) { m_surfaceLabel = "Server Browser"; - memset(m_szServerAddressBuffer, '\0', sizeof(m_szServerAddressBuffer)); - memset(m_szServerEncKeyBuffer, '\0', sizeof(m_szServerEncKeyBuffer)); + memset(m_serverAddressTextBuf, '\0', sizeof(m_serverAddressTextBuf)); + memset(m_serverNetKeyTextBuf, '\0', sizeof(m_serverNetKeyTextBuf)); - m_rLockedIconBlob = GetModuleResource(IDB_PNG2); + m_lockedIconDataResource = GetModuleResource(IDB_PNG2); } //----------------------------------------------------------------------------- @@ -62,10 +62,7 @@ CBrowser::CBrowser(void) //----------------------------------------------------------------------------- CBrowser::~CBrowser(void) { - if (m_idLockedIcon) - { - m_idLockedIcon->Release(); - } + Shutdown(); } //----------------------------------------------------------------------------- @@ -75,13 +72,24 @@ bool CBrowser::Init(void) { SetStyleVar(928.f, 524.f, -500.f, 50.f); - bool ret = LoadTextureBuffer(reinterpret_cast(m_rLockedIconBlob.m_pData), int(m_rLockedIconBlob.m_nSize), - &m_idLockedIcon, &m_rLockedIconBlob.m_nWidth, &m_rLockedIconBlob.m_nHeight); + bool ret = LoadTextureBuffer(reinterpret_cast(m_lockedIconDataResource.m_pData), int(m_lockedIconDataResource.m_nSize), + &m_lockedIconShaderResource, &m_lockedIconDataResource.m_nWidth, &m_lockedIconDataResource.m_nHeight); - IM_ASSERT(ret && m_idLockedIcon); + IM_ASSERT(ret && m_lockedIconShaderResource); return ret; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBrowser::Shutdown(void) +{ + if (m_lockedIconShaderResource) + { + m_lockedIconShaderResource->Release(); + } +} + //----------------------------------------------------------------------------- // Purpose: draws the main browser front-end //----------------------------------------------------------------------------- @@ -102,36 +110,29 @@ void CBrowser::RunFrame(void) Animate(); RunTask(); - int nVars = 0; - float flWidth; - float flHeight; + int baseWindowStyleVars = 0; + ImVec2 minBaseWindowRect; + if (m_surfaceStyle == ImGuiStyle_t::MODERN) { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); baseWindowStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++; - flWidth = 621.f; - flHeight = 532.f; + minBaseWindowRect = ImVec2(621.f, 532.f); } else { - if (m_surfaceStyle == ImGuiStyle_t::LEGACY) - { - flWidth = 619.f; - flHeight = 526.f; - } - else - { - flWidth = 618.f; - flHeight = 524.f; - } + 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 }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); baseWindowStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++; - ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f); baseWindowStyleVars++; } - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(flWidth, flHeight)); nVars++; + + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, minBaseWindowRect); baseWindowStyleVars++; if (m_activated && m_reclaimFocus) // Reclaim focus on window apparition. { @@ -139,17 +140,8 @@ void CBrowser::RunFrame(void) m_reclaimFocus = false; } - if (!ImGui::Begin(m_surfaceLabel, &m_activated, ImGuiWindowFlags_NoScrollbar, &ResetInput)) - { - ImGui::End(); - ImGui::PopStyleVar(nVars); - return; - } - DrawSurface(); - - ImGui::End(); - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(baseWindowStyleVars); } //----------------------------------------------------------------------------- @@ -174,38 +166,47 @@ void CBrowser::RunTask() if (m_activated) { - if (m_bQueryListNonRecursive) + if (m_queryNewListNonRecursive) { - m_bQueryListNonRecursive = false; + m_queryNewListNonRecursive = false; RefreshServerList(); } } else // Refresh server list the next time 'm_activated' evaluates to true. { - m_bReclaimFocusTokenField = true; - m_bQueryListNonRecursive = true; + m_reclaimFocusOnTokenField = true; + m_queryNewListNonRecursive = true; } } //----------------------------------------------------------------------------- -// Purpose: draws the compmenu +// Purpose: draws the server browser's main surface +// Output : true if a frame has been drawn, false otherwise //----------------------------------------------------------------------------- bool CBrowser::DrawSurface(void) { + if (!ImGui::Begin(m_surfaceLabel, &m_activated, ImGuiWindowFlags_NoScrollbar, &ResetInput)) + { + ImGui::End(); + return false; + } + ImGui::BeginTabBar("CompMenu"); if (ImGui::BeginTabItem("Browsing")) { - BrowserPanel(); + DrawBrowserPanel(); ImGui::EndTabItem(); } #ifndef CLIENT_DLL if (ImGui::BeginTabItem("Hosting")) { - HostPanel(); + DrawHostPanel(); ImGui::EndTabItem(); } #endif // !CLIENT_DLL + ImGui::EndTabBar(); + ImGui::End(); return true; } @@ -213,38 +214,38 @@ bool CBrowser::DrawSurface(void) //----------------------------------------------------------------------------- // Purpose: draws the server browser section //----------------------------------------------------------------------------- -void CBrowser::BrowserPanel(void) +void CBrowser::DrawBrowserPanel(void) { ImGui::BeginGroup(); - m_imServerBrowserFilter.Draw(); + m_serverBrowserTextFilter.Draw(); ImGui::SameLine(); if (ImGui::Button("Refresh")) { - m_svServerListMessage.clear(); + m_serverListMessage.clear(); RefreshServerList(); } ImGui::EndGroup(); - ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), "%s", m_svServerListMessage.c_str()); + ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), "%s", m_serverListMessage.c_str()); ImGui::Separator(); - int iVars = 0; // Eliminate borders around server list table. - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 0.f }); iVars++; + int windowStyleVars = 0; // Eliminate borders around server list table. + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 0.f }); windowStyleVars++; const float fFooterHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); ImGui::BeginChild("##ServerBrowser_ServerList", { 0, -fFooterHeight }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar); if (ImGui::BeginTable("##ServerBrowser_ServerListTable", 6, ImGuiTableFlags_Resizable)) { - int nVars = 0; + int frameStyleVars = 0; if (m_surfaceStyle == ImGuiStyle_t::MODERN) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 8.f, 0.f }); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 8.f, 0.f }); frameStyleVars++; } else { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4.f, 0.f }); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4.f, 0.f }); frameStyleVars++; } ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 25); @@ -265,9 +266,9 @@ void CBrowser::BrowserPanel(void) char pszHostPort[32]; sprintf(pszHostPort, "%d", server.port); - if (m_imServerBrowserFilter.PassFilter(pszHostName) - || m_imServerBrowserFilter.PassFilter(pszHostMap) - || m_imServerBrowserFilter.PassFilter(pszHostPort)) + if (m_serverBrowserTextFilter.PassFilter(pszHostName) + || m_serverBrowserTextFilter.PassFilter(pszHostMap) + || m_serverBrowserTextFilter.PassFilter(pszHostPort)) { ImGui::TableNextColumn(); ImGui::Text("%s", pszHostName); @@ -297,11 +298,11 @@ void CBrowser::BrowserPanel(void) g_ServerListManager.m_Mutex.unlock(); ImGui::EndTable(); - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(frameStyleVars); } ImGui::EndChild(); - ImGui::PopStyleVar(iVars); + ImGui::PopStyleVar(windowStyleVars); ImGui::Separator(); @@ -315,17 +316,17 @@ void CBrowser::BrowserPanel(void) ImGui::PushItemWidth(itemWidth); { - ImGui::InputTextWithHint("##ServerBrowser_ServerCon", "Server address and port", m_szServerAddressBuffer, IM_ARRAYSIZE(m_szServerAddressBuffer)); + ImGui::InputTextWithHint("##ServerBrowser_ServerCon", "Server address and port", m_serverAddressTextBuf, IM_ARRAYSIZE(m_serverAddressTextBuf)); ImGui::SameLine(); - ImGui::InputTextWithHint("##ServerBrowser_ServerKey", "Encryption key", m_szServerEncKeyBuffer, IM_ARRAYSIZE(m_szServerEncKeyBuffer)); + ImGui::InputTextWithHint("##ServerBrowser_ServerKey", "Encryption key", m_serverNetKeyTextBuf, IM_ARRAYSIZE(m_serverNetKeyTextBuf)); ImGui::SameLine(); if (ImGui::Button("Connect", ImVec2(itemWidth, ImGui::GetFrameHeight()))) { - if (m_szServerAddressBuffer[0]) + if (m_serverAddressTextBuf[0]) { - g_ServerListManager.ConnectToServer(m_szServerAddressBuffer, m_szServerEncKeyBuffer); + g_ServerListManager.ConnectToServer(m_serverAddressTextBuf, m_serverNetKeyTextBuf); } } @@ -351,15 +352,16 @@ void CBrowser::RefreshServerList(void) // Thread the request, and let the main thread assign status message back std::thread request([&] { - std::string svServerListMessage; - g_ServerListManager.RefreshServerList(svServerListMessage); + std::string serverListMessage; + g_ServerListManager.RefreshServerList(serverListMessage); - g_TaskQueue.Dispatch([&, svServerListMessage] + g_TaskQueue.Dispatch([&, serverListMessage] { - SetServerListMessage(svServerListMessage.c_str()); + SetServerListMessage(serverListMessage.c_str()); }, 0); } ); + request.detach(); } @@ -368,32 +370,31 @@ void CBrowser::RefreshServerList(void) //----------------------------------------------------------------------------- void CBrowser::HiddenServersModal(void) { - float flHeight; // Set the padding accordingly for each theme. + float modalWindowHeight; // Set the padding accordingly for each theme. switch (m_surfaceStyle) { case ImGuiStyle_t::LEGACY: - flHeight = 207.f; + modalWindowHeight = 207.f; break; case ImGuiStyle_t::MODERN: - flHeight = 214.f; + modalWindowHeight = 214.f; break; default: - flHeight = 206.f; + modalWindowHeight = 206.f; break; } - int nVars = 0; - bool bModalOpen = true; + int modalStyleVars = 0; + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(408.f, modalWindowHeight)); modalStyleVars++; - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(408.f, flHeight)); nVars++; - - if (ImGui::BeginPopupModal("Private Server", &bModalOpen, ImGuiWindowFlags_NoResize)) + bool isModalStillOpen = true; + if (ImGui::BeginPopupModal("Private Server", &isModalStillOpen, ImGuiWindowFlags_NoResize)) { - ImGui::SetWindowSize(ImVec2(408.f, flHeight), ImGuiCond_Always); + ImGui::SetWindowSize(ImVec2(408.f, modalWindowHeight), ImGuiCond_Always); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. - ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(float(m_rLockedIconBlob.m_nWidth), float(m_rLockedIconBlob.m_nHeight))); - ImGui::Image(m_idLockedIcon, ImVec2(float(m_rLockedIconBlob.m_nWidth), float(m_rLockedIconBlob.m_nHeight))); // Display texture. + ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(float(m_lockedIconDataResource.m_nWidth), float(m_lockedIconDataResource.m_nHeight))); + ImGui::Image(m_lockedIconShaderResource, ImVec2(float(m_lockedIconDataResource.m_nWidth), float(m_lockedIconDataResource.m_nHeight))); // Display texture. ImGui::EndChild(); ImGui::PopStyleColor(); // Pop the override for the child bg. @@ -409,82 +410,82 @@ void CBrowser::HiddenServersModal(void) ImGui::PopItemWidth(); - if (m_bReclaimFocusTokenField) + if (m_reclaimFocusOnTokenField) { ImGui::SetKeyboardFocusHere(-1); // -1 means previous widget. - m_bReclaimFocusTokenField = false; + m_reclaimFocusOnTokenField = false; } ImGui::Dummy(ImVec2(contentRegionMax.x, 19.f)); // Place a dummy, basically making space inserting a blank element. - ImGui::TextColored(m_ivHiddenServerMessageColor, "%s", m_svHiddenServerRequestMessage.c_str()); + ImGui::TextColored(m_hiddenServerMessageColor, "%s", m_hiddenServerRequestMessage.c_str()); ImGui::Separator(); if (ImGui::Button("Connect", ImVec2(contentRegionMax.x, 24))) { - m_svHiddenServerRequestMessage.clear(); - m_bReclaimFocusTokenField = true; + m_hiddenServerRequestMessage.clear(); + m_reclaimFocusOnTokenField = true; if (!hiddenServerToken.empty()) { NetGameServer_t server; - bool result = g_MasterServer.GetServerByToken(server, m_svHiddenServerRequestMessage, hiddenServerToken); // Send token connect request. + const bool result = g_MasterServer.GetServerByToken(server, m_hiddenServerRequestMessage, hiddenServerToken); // Send token connect request. if (result && !server.name.empty()) { g_ServerListManager.ConnectToServer(server.address, server.port, server.netKey); // Connect to the server - m_svHiddenServerRequestMessage = Format("Found server: %s", server.name.c_str()); - m_ivHiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + m_hiddenServerRequestMessage = Format("Found server: %s", server.name.c_str()); + m_hiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); ImGui::CloseCurrentPopup(); } else { - if (m_svHiddenServerRequestMessage.empty()) + if (m_hiddenServerRequestMessage.empty()) { - m_svHiddenServerRequestMessage = "Unknown error."; + m_hiddenServerRequestMessage = "Unknown error."; } else // Display error message. { - m_svHiddenServerRequestMessage = Format("Error: %s", m_svHiddenServerRequestMessage.c_str()); + m_hiddenServerRequestMessage = Format("Error: %s", m_hiddenServerRequestMessage.c_str()); } - m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } else { - m_svHiddenServerRequestMessage = "Token is required."; - m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hiddenServerRequestMessage = "Token is required."; + m_hiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } if (ImGui::Button("Close", ImVec2(contentRegionMax.x, 24))) { - m_svHiddenServerRequestMessage.clear(); - m_bReclaimFocusTokenField = true; + m_hiddenServerRequestMessage.clear(); + m_reclaimFocusOnTokenField = true; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(modalStyleVars); } - else if (!bModalOpen) + else if (!isModalStillOpen) { - m_svHiddenServerRequestMessage.clear(); - m_bReclaimFocusTokenField = true; + m_hiddenServerRequestMessage.clear(); + m_reclaimFocusOnTokenField = true; - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(modalStyleVars); } else { - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(modalStyleVars); } } //----------------------------------------------------------------------------- // Purpose: draws the host section //----------------------------------------------------------------------------- -void CBrowser::HostPanel(void) +void CBrowser::DrawHostPanel(void) { #ifndef CLIENT_DLL std::lock_guard l(g_ServerListManager.m_Mutex); @@ -527,10 +528,10 @@ void CBrowser::HostPanel(void) ImGui::EndCombo(); } - m_bQueryGlobalBanList = sv_globalBanlist.GetBool(); // Sync toggle with 'sv_globalBanlist'. - if (ImGui::Checkbox("Load global banned list", &m_bQueryGlobalBanList)) + m_queryGlobalBanList = sv_globalBanlist.GetBool(); // Sync toggle with 'sv_globalBanlist'. + if (ImGui::Checkbox("Load global banned list", &m_queryGlobalBanList)) { - sv_globalBanlist.SetValue(m_bQueryGlobalBanList); + sv_globalBanlist.SetValue(m_queryGlobalBanList); } ImGui::Text("Server visibility"); @@ -548,47 +549,51 @@ void CBrowser::HostPanel(void) g_ServerListManager.m_ServerVisibility = EServerVisibility_t::PUBLIC; } - ImGui::TextColored(m_HostMessageColor, "%s", m_svHostMessage.c_str()); - if (!m_svHostToken.empty()) + ImGui::TextColored(m_hostMessageColor, "%s", m_hostMessage.c_str()); + if (!m_hostToken.empty()) { - ImGui::InputText("##ServerHost_HostToken", &m_svHostToken, ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##ServerHost_HostToken", &m_hostToken, ImGuiInputTextFlags_ReadOnly); } ImGui::Spacing(); const ImVec2 contentRegionMax = ImGui::GetContentRegionAvail(); - const bool bServerActive = g_pServer->IsActive(); + const bool serverActive = g_pServer->IsActive(); if (!g_pHostState->m_bActiveGame) { if (ImGui::Button("Start server", ImVec2(contentRegionMax.x, 32))) { - m_svHostMessage.clear(); + m_hostMessage.clear(); - bool bEnforceField = g_ServerListManager.m_ServerVisibility == EServerVisibility_t::OFFLINE ? true : !g_ServerListManager.m_Server.name.empty(); - if (bEnforceField && !g_ServerListManager.m_Server.playlist.empty() && !g_ServerListManager.m_Server.map.empty()) + const bool enforceField = g_ServerListManager.m_ServerVisibility == EServerVisibility_t::OFFLINE + ? true + : !g_ServerListManager.m_Server.name.empty(); + + if (enforceField && !g_ServerListManager.m_Server.playlist.empty() && !g_ServerListManager.m_Server.map.empty()) { - g_ServerListManager.LaunchServer(bServerActive); // Launch server. + g_ServerListManager.LaunchServer(serverActive); // Launch server. } else { if (g_ServerListManager.m_Server.name.empty()) { - m_svHostMessage = "Server name is required."; - m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Server name is required."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } else if (g_ServerListManager.m_Server.playlist.empty()) { - m_svHostMessage = "Playlist is required."; - m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Playlist is required."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } else if (g_ServerListManager.m_Server.map.empty()) { - m_svHostMessage = "Level name is required."; - m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Level name is required."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } } + if (ImGui::Button("Reload playlist", ImVec2(contentRegionMax.x, 32))) { v_Playlists_Download_f(); @@ -612,16 +617,16 @@ void CBrowser::HostPanel(void) { if (!g_ServerListManager.m_Server.map.empty()) { - g_ServerListManager.LaunchServer(bServerActive); + g_ServerListManager.LaunchServer(serverActive); } else { - m_svHostMessage = "Failed to change level: 'levelname' was empty."; - m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Failed to change level: 'levelname' was empty."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } - if (bServerActive) + if (serverActive) { ImGui::Spacing(); ImGui::Separator(); @@ -677,16 +682,16 @@ void CBrowser::UpdateHostingStatus(void) { case EHostStatus_t::NOT_HOSTING: { - if (!m_svHostToken.empty()) + if (!m_hostToken.empty()) { - m_svHostToken.clear(); + m_hostToken.clear(); } - if (ImGui::ColorConvertFloat4ToU32(m_HostMessageColor) == // Only clear if this is green (a valid hosting message). + if (ImGui::ColorConvertFloat4ToU32(m_hostMessageColor) == // Only clear if this is green (a valid hosting message). ImGui::ColorConvertFloat4ToU32(ImVec4(0.00f, 1.00f, 0.00f, 1.00f))) { - m_svHostMessage.clear(); - m_HostMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + m_hostMessage.clear(); + m_hostMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); } break; @@ -780,8 +785,8 @@ void CBrowser::SendHostingPostRequest(const NetGameServer_t& gameServer) void CBrowser::InstallHostingDetails(const bool postFailed, const char* const hostMessage, const char* const hostToken, const string& hostIp) { #ifndef CLIENT_DLL - m_svHostMessage = hostMessage; - m_svHostToken = hostToken; + m_hostMessage = hostMessage; + m_hostToken = hostToken; if (!hostIp.empty()) { @@ -790,18 +795,19 @@ void CBrowser::InstallHostingDetails(const bool postFailed, const char* const ho if (postFailed) { - m_HostMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + m_hostMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); stringstream ssMessage; - ssMessage << "Broadcasting: "; - if (!m_svHostToken.empty()) + ssMessage << "Broadcasting"; + if (!m_hostToken.empty()) { - ssMessage << "share the following token for clients to connect: "; + ssMessage << ": share the following token for clients to connect: "; } - m_svHostMessage = ssMessage.str(); + + m_hostMessage = ssMessage.str(); } else { - m_HostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } #endif // !CLIENT_DLL } diff --git a/r5dev/gameui/IBrowser.h b/r5dev/gameui/IBrowser.h index d11721c6..9591892d 100644 --- a/r5dev/gameui/IBrowser.h +++ b/r5dev/gameui/IBrowser.h @@ -15,17 +15,18 @@ public: virtual ~CBrowser(void); virtual bool Init(void); + virtual void Shutdown(void); virtual void RunFrame(void); void RunTask(void); virtual bool DrawSurface(void); - void BrowserPanel(void); + void DrawBrowserPanel(void); void RefreshServerList(void); void HiddenServersModal(void); - void HostPanel(void); + void DrawHostPanel(void); void UpdateHostingStatus(void); void InstallHostingDetails(const bool postFailed, const char* const hostMessage, const char* const hostToken, const string& hostIp); @@ -38,38 +39,38 @@ public: static void ToggleBrowser_f(); private: - inline void SetServerListMessage(const char* const message) { m_svServerListMessage = message; }; - inline void SetHostMessage(const char* const message) { m_svHostMessage = message; } - inline void SetHostToken(const char* const message) { m_svHostToken = message; } + inline void SetServerListMessage(const char* const message) { m_serverListMessage = message; }; + inline void SetHostMessage(const char* const message) { m_hostMessage = message; } + inline void SetHostToken(const char* const message) { m_hostToken = message; } private: - bool m_bReclaimFocusTokenField; - bool m_bQueryListNonRecursive; // When set, refreshes the server list once the next frame. - bool m_bQueryGlobalBanList; - char m_szServerAddressBuffer[128]; - char m_szServerEncKeyBuffer[30]; + bool m_reclaimFocusOnTokenField; + bool m_queryNewListNonRecursive; // When set, refreshes the server list once the next frame. + bool m_queryGlobalBanList; + char m_serverAddressTextBuf[128]; + char m_serverNetKeyTextBuf[30]; - ID3D11ShaderResourceView* m_idLockedIcon; - MODULERESOURCE m_rLockedIconBlob; + ID3D11ShaderResourceView* m_lockedIconShaderResource; + MODULERESOURCE m_lockedIconDataResource; //////////////////// // Server List // //////////////////// - ImGuiTextFilter m_imServerBrowserFilter; - string m_svServerListMessage; + ImGuiTextFilter m_serverBrowserTextFilter; + string m_serverListMessage; //////////////////// // Host Server // //////////////////// - string m_svHostMessage; - string m_svHostToken; - ImVec4 m_HostMessageColor; + string m_hostMessage; + string m_hostToken; + ImVec4 m_hostMessageColor; //////////////////// // Private Server // //////////////////// - string m_svHiddenServerRequestMessage; - ImVec4 m_ivHiddenServerMessageColor; + string m_hiddenServerRequestMessage; + ImVec4 m_hiddenServerMessageColor; }; extern CBrowser g_Browser; diff --git a/r5dev/gameui/IConsole.cpp b/r5dev/gameui/IConsole.cpp index 3d49bd26..30e18dea 100644 --- a/r5dev/gameui/IConsole.cpp +++ b/r5dev/gameui/IConsole.cpp @@ -31,9 +31,9 @@ static ConVar con_max_lines("con_max_lines", "1024", FCVAR_DEVELOPMENTONLY | FCV 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_showhelptext("con_suggest_showhelptext", "1", FCVAR_DEVELOPMENTONLY, "Show CommandBase help text in autocomplete window"); -static ConVar con_suggest_showflags("con_suggest_showflags", "1", FCVAR_DEVELOPMENTONLY, "Show CommandBase flags in autocomplete window"); +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); @@ -51,24 +51,21 @@ static ConCommand con_clearhistory("con_clearhistory", CConsole::ClearHistory_f, // Purpose: //----------------------------------------------------------------------------- CConsole::CConsole(void) - : m_pszLoggingLabel("LoggingRegion") - , m_nHistoryPos(PositionMode_t::kPark) - , m_nSuggestPos(PositionMode_t::kPark) - , m_nScrollBack(0) - , m_nSelectBack(0) - , m_nInputTextLen(0) - , m_flScrollX(0.f) - , m_flScrollY(0.f) - , m_bCopyToClipBoard(false) - , m_bModifyInput(false) - , m_bCanAutoComplete(false) - , m_bSuggestActive(false) - , m_bSuggestMoved(false) - , m_bSuggestUpdate(false) + : 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"; - m_nInputFlags = + m_inputTextFieldFlags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | @@ -77,7 +74,7 @@ CConsole::CConsole(void) ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_AutoCaretEnd; - m_nSuggestFlags = + m_autoCompleteWindowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | @@ -85,15 +82,14 @@ CConsole::CConsole(void) ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar; - m_nLoggingFlags = + m_colorLoggerWindowFlags = ImGuiWindowFlags_NoMove | ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoNavInputs; - memset(m_szInputBuf, '\0', sizeof(m_szInputBuf)); - memset(m_szWindowLabel, '\0', sizeof(m_szWindowLabel)); - snprintf(m_szSummary, sizeof(m_szSummary), "%zu history items", m_vHistory.size()); + memset(m_inputTextBuf, '\0', sizeof(m_inputTextBuf)); + snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "%zu history items", m_vecHistory.size()); } //----------------------------------------------------------------------------- @@ -101,17 +97,11 @@ CConsole::CConsole(void) //----------------------------------------------------------------------------- CConsole::~CConsole(void) { - for (MODULERESOURCE& flagIcon : m_vFlagIcons) - { - if (flagIcon.m_idIcon) - { - flagIcon.m_idIcon->Release(); - } - } + Shutdown(); } //----------------------------------------------------------------------------- -// Purpose: game console setup +// Purpose: game console initialization // Output : true on success, false otherwise //----------------------------------------------------------------------------- bool CConsole::Init(void) @@ -120,6 +110,20 @@ bool CConsole::Init(void) 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 //----------------------------------------------------------------------------- @@ -131,53 +135,42 @@ void CConsole::RunFrame(void) //ImGui::ShowDemoWindow(); } - bool drawn = false; - /************************** * BASE PANEL SETUP * **************************/ + if (!m_initialized) { - if (!m_initialized) - { - Init(); - m_initialized = true; - } - - Animate(); - - int nVars = 0; - float flWidth; - float flHeight; - if (m_surfaceStyle == ImGuiStyle_t::MODERN) - { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); nVars++; - - flWidth = 621.f; - flHeight = 532.f; - } - else - { - if (m_surfaceStyle == ImGuiStyle_t::LEGACY) - { - flWidth = 619.f; - flHeight = 526.f; - } - else - { - flWidth = 618.f; - flHeight = 524.f; - } - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); nVars++; - } - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(flWidth, flHeight)); nVars++; - - drawn = DrawSurface(); - ImGui::PopStyleVar(nVars); + 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; @@ -185,42 +178,36 @@ void CConsole::RunFrame(void) /************************** * SUGGESTION PANEL SETUP * **************************/ + if (RunAutoComplete()) { - int nVars = 0; - if (AutoComplete()) + if (m_surfaceStyle == ImGuiStyle_t::MODERN) { - if (m_surfaceStyle == ImGuiStyle_t::MODERN) - { - const ImGuiStyle& style = ImGui::GetStyle(); - m_ivSuggestWindowPos.y = m_ivSuggestWindowPos.y + style.WindowPadding.y + 1.5f; - } - - ImGui::SetNextWindowPos(m_ivSuggestWindowPos); - ImGui::SetNextWindowSize(m_ivSuggestWindowSize); - if (m_bSuggestUpdate) - { - ImGui::SetNextWindowScroll(ImVec2(0.f, 0.f)); - m_bSuggestUpdate = false; - } - - // 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)); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); nVars++; - - SuggestPanel(); - - ImGui::PopStyleVar(nVars); + 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 -// Input : *bDraw - +// Output : true if a frame has been drawn, false otherwise //----------------------------------------------------------------------------- bool CConsole::DrawSurface(void) { @@ -233,15 +220,15 @@ bool CConsole::DrawSurface(void) const ImGuiStyle& style = ImGui::GetStyle(); // Reserve enough left-over height and width for 1 separator + 1 input text - const float flFooterHeightReserve = style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - const float flFooterWidthReserve = style.ItemSpacing.y + ImGui::GetWindowWidth(); + const float footerHeightReserve = style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + const float footerWidthReserve = style.ItemSpacing.y + ImGui::GetWindowWidth(); - ImVec2 fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr); + const ImVec2 fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr); /////////////////////////////////////////////////////////////////////// ImGui::Separator(); if (ImGui::BeginPopup("Options")) { - OptionsPanel(); + DrawOptionsPanel(); } if (ImGui::Button("Options")) { @@ -249,72 +236,72 @@ bool CConsole::DrawSurface(void) } ImGui::SameLine(); - m_Logger.GetFilter().Draw("Filter | ", flFooterWidthReserve - 500); + m_colorTextLogger.GetFilter().Draw("Filter | ", footerWidthReserve - 500); ImGui::SameLine(); - ImGui::Text("%s", m_szSummary); + ImGui::Text("%s", m_summaryTextBuf); ImGui::Separator(); /////////////////////////////////////////////////////////////////////// - if (!m_Logger.IsScrolledToBottom() && m_nScrollBack > 0) + if (!m_colorTextLogger.IsScrolledToBottom() && m_scrollBackAmount > 0) { - ImGuiWindow* pWindow = ImGui::GetCurrentWindow(); - ImGuiID nID = pWindow->GetID(m_pszLoggingLabel); + ImGuiWindow* const window = ImGui::GetCurrentWindow(); + const ImGuiID windowId = window->GetID(m_loggerLabel); - snprintf(m_szWindowLabel, sizeof(m_szWindowLabel), "%s/%s_%08X", m_surfaceLabel, m_pszLoggingLabel, nID); - ImGui::SetWindowScrollY(m_szWindowLabel, m_flScrollY - m_nScrollBack * fontSize.y); + 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_nScrollBack = 0; + m_scrollBackAmount = 0; /////////////////////////////////////////////////////////////////////// - int iVars = 0; // Eliminate borders around log window. - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 1.f }); iVars++; - ImGui::BeginChild(m_pszLoggingLabel, ImVec2(0, -flFooterHeightReserve), true, m_nLoggingFlags); + int numStyleVars = 0; // Eliminate borders around log window. + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 1.f }); numStyleVars++; + ImGui::BeginChild(m_loggerLabel, ImVec2(0, -footerHeightReserve), true, m_colorLoggerWindowFlags); - AUTO_LOCK(m_Mutex); - m_Logger.Render(); - - if (m_bCopyToClipBoard) + // 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. { - m_Logger.Copy(true); - m_bCopyToClipBoard = false; + AUTO_LOCK(m_colorTextLoggerMutex); + m_colorTextLogger.Render(); } - m_flScrollX = ImGui::GetScrollX(); - m_flScrollY = ImGui::GetScrollY(); + m_lastFrameScrollPos = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); ImGui::EndChild(); - ImGui::PopStyleVar(iVars); + ImGui::PopStyleVar(numStyleVars); ImGui::Separator(); - std::function fnHandleInput = [&](void) + const std::function fnHandleInput = [&](void) { - if (m_szInputBuf[0]) + if (m_inputTextBuf[0]) { - ProcessCommand(m_szInputBuf); - ResetAutoComplete(); + ProcessCommand(m_inputTextBuf); + ResetAutoCompleteData(); - m_bModifyInput = true; + m_inputTextBufModified = true; } - BuildSummary(); + BuildSummaryText(""); m_reclaimFocus = true; }; /////////////////////////////////////////////////////////////////////// - ImGui::PushItemWidth(flFooterWidthReserve - 80); - if (ImGui::InputText("##input", m_szInputBuf, IM_ARRAYSIZE(m_szInputBuf), m_nInputFlags, &TextEditCallbackStub, reinterpret_cast(this))) + ImGui::PushItemWidth(footerWidthReserve - 80); + if (ImGui::InputText("##input", m_inputTextBuf, IM_ARRAYSIZE(m_inputTextBuf), m_inputTextFieldFlags, &TextEditCallbackStub, reinterpret_cast(this))) { // If we selected something in the suggestions window, create the // command from that instead - if (m_nSuggestPos > PositionMode_t::kPark) + if (m_suggestPos > ConAutoCompletePos_e::kPark) { - BuildInputFromSelected(m_vSuggest[m_nSuggestPos], m_svInputConVar); - BuildSummary(m_svInputConVar); + DetermineInputTextFromSelectedSuggestion(m_vecSuggest[m_suggestPos], m_selectedSuggestionText); + BuildSummaryText(m_selectedSuggestionText.c_str()); - m_bModifyInput = true; + m_inputTextBufModified = true; m_reclaimFocus = true; } else @@ -333,7 +320,7 @@ bool CConsole::DrawSurface(void) m_reclaimFocus = false; } - BuildSuggestPanelRect(); + DetermineAutoCompleteWindowRect(); ImGui::SameLine(); if (ImGui::Button("Submit")) @@ -348,9 +335,9 @@ bool CConsole::DrawSurface(void) //----------------------------------------------------------------------------- // Purpose: draws the options panel //----------------------------------------------------------------------------- -void CConsole::OptionsPanel(void) +void CConsole::DrawOptionsPanel(void) { - ImGui::Checkbox("Auto-scroll", &m_Logger.m_bAutoScroll); + ImGui::Checkbox("Auto-scroll", &m_colorTextLogger.m_bAutoScroll); ImGui::SameLine(); ImGui::PushItemWidth(100); @@ -363,7 +350,13 @@ void CConsole::OptionsPanel(void) } ImGui::SameLine(); - m_bCopyToClipBoard = ImGui::SmallButton("Copy"); + + // 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(); @@ -385,35 +378,36 @@ void CConsole::OptionsPanel(void) } //----------------------------------------------------------------------------- -// Purpose: draws the suggestion panel with results based on user input +// Purpose: draws the autocomplete panel with results based on user input //----------------------------------------------------------------------------- -void CConsole::SuggestPanel(void) +void CConsole::DrawAutoCompletePanel(void) { - ImGui::Begin("##suggest", nullptr, m_nSuggestFlags); + ImGui::Begin("##suggest", nullptr, m_autoCompleteWindowFlags); ImGui::PushAllowKeyboardFocus(false); - for (size_t i = 0, ns = m_vSuggest.size(); i < ns; i++) + for (size_t i = 0, ns = m_vecSuggest.size(); i < ns; i++) { - const CSuggest& suggest = m_vSuggest[i]; - const bool bIsIndexActive = m_nSuggestPos == ssize_t(i); + const ConAutoCompleteSuggest_s& suggest = m_vecSuggest[i]; + const bool isIndexActive = m_suggestPos == ssize_t(i); ImGui::PushID(static_cast(i)); - if (con_suggest_showflags.GetBool()) + if (con_autocomplete_window_textures.GetBool()) { // Show the flag texture before the cvar name. - const int mainTexIdx = GetFlagTextureIndex(suggest.m_nFlags); - const MODULERESOURCE& mainRes = m_vFlagIcons[mainTexIdx]; + 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.m_nFlags != COMMAND_COMPLETION_MARKER) + suggest.flags != COMMAND_COMPLETION_MARKER) { - std::function fnAddHint = [&](const ConVarFlags::FlagDesc_t& cvarInfo) + const std::function fnAddHint = [&](const ConVarFlags::FlagDesc_t& cvarInfo) { const int hintTexIdx = GetFlagTextureIndex(cvarInfo.bit); - const MODULERESOURCE& hintRes = m_vFlagIcons[hintTexIdx]; + const MODULERESOURCE& hintRes = m_vecFlagIcons[hintTexIdx]; ImGui::Image(hintRes.m_idIcon, ImVec2(float(hintRes.m_nWidth), float(hintRes.m_nHeight))); ImGui::SameLine(); @@ -421,19 +415,19 @@ void CConsole::SuggestPanel(void) }; ImGui::BeginTooltip(); - bool bFlagSet = false; + 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.m_nFlags & info.bit) + if (suggest.flags & info.bit) { - bFlagSet = true; + isFlagSet = true; fnAddHint(info); } } - if (!bFlagSet) // Display the FCVAR_NONE flag if no flags are set. + if (!isFlagSet) // Display the FCVAR_NONE flag if no flags are set. { fnAddHint(g_ConVarFlags.m_FlagsToDesc[0]); } @@ -444,31 +438,31 @@ void CConsole::SuggestPanel(void) ImGui::SameLine(); } - if (ImGui::Selectable(suggest.m_svName.c_str(), bIsIndexActive)) + if (ImGui::Selectable(suggest.text.c_str(), isIndexActive)) { ImGui::Separator(); - string svConVar; + string newInputText; - BuildInputFromSelected(suggest, svConVar); - memmove(m_szInputBuf, svConVar.data(), svConVar.size() + 1); + DetermineInputTextFromSelectedSuggestion(suggest, newInputText); + memmove(m_inputTextBuf, newInputText.data(), newInputText.size() + 1); - m_bCanAutoComplete = true; + m_canAutoComplete = true; m_reclaimFocus = true; - BuildSummary(svConVar); + BuildSummaryText(newInputText.c_str()); } ImGui::PopID(); // Update the suggest position - if (m_bSuggestMoved) + if (m_autoCompletePosMoved) { - if (bIsIndexActive) // Bring the 'active' element into view + if (isIndexActive) // Bring the 'active' element into view { ImGuiWindow* const pWindow = ImGui::GetCurrentWindow(); ImRect imRect = ImGui::GetCurrentContext()->LastItemData.Rect; - // Reset to keep flag in display. + // Reset to keep flag icon in display. imRect.Min.x = pWindow->InnerRect.Min.x; imRect.Max.x = pWindow->InnerRect.Max.x; @@ -477,15 +471,15 @@ void CConsole::SuggestPanel(void) imRect.Max.y -= 1; ImGui::ScrollToRect(pWindow, imRect); - m_bSuggestMoved = false; + m_autoCompletePosMoved = false; } - else if (m_nSuggestPos == PositionMode_t::kPark) + else if (m_suggestPos == ConAutoCompletePos_e::kPark) { // Reset position; kPark = no active element. ImGui::SetScrollX(0.0f); ImGui::SetScrollY(0.0f); - m_bSuggestMoved = false; + m_autoCompletePosMoved = false; } } } @@ -498,37 +492,38 @@ void CConsole::SuggestPanel(void) // Purpose: runs the auto complete for the console // Output : true if auto complete is performed, false otherwise //----------------------------------------------------------------------------- -bool CConsole::AutoComplete(void) +bool CConsole::RunAutoComplete(void) { // Don't suggest if user tries to assign value to ConVar or execute ConCommand. - if (!m_szInputBuf[0] || strstr(m_szInputBuf, ";")) + if (!m_inputTextBuf[0] || strstr(m_inputTextBuf, ";")) { - if (m_bSuggestActive) + if (m_autoCompleteActive) { - ResetAutoComplete(); + ResetAutoCompleteData(); } + return false; } - if (!strstr(m_szInputBuf, " ")) + if (!strstr(m_inputTextBuf, " ")) { - if (m_bCanAutoComplete) + if (m_canAutoComplete) { - FindFromPartial(); + CreateSuggestionsFromPartial(); } } - else if (m_bCanAutoComplete) // Command completion callback. + else if (m_canAutoComplete) // Command completion callback. { - ResetAutoComplete(); + ResetAutoCompleteData(); - char szCommand[sizeof(m_szInputBuf)]; + 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_szInputBuf); i++) + for (; i < sizeof(m_inputTextBuf); i++) { - const char c = m_szInputBuf[i]; + const char c = m_inputTextBuf[i]; if (c == '\0' || isspace(c)) { @@ -539,12 +534,12 @@ bool CConsole::AutoComplete(void) } szCommand[i] = '\0'; - ConCommand* pCommand = g_pCVar->FindCommand(szCommand); + ConCommand* const pCommand = g_pCVar->FindCommand(szCommand); if (pCommand && pCommand->CanAutoComplete()) { CUtlVector< CUtlString > commands; - int iret = pCommand->AutoCompleteSuggest(m_szInputBuf, commands); + const int iret = pCommand->AutoCompleteSuggest(m_inputTextBuf, commands); if (!iret) { @@ -553,7 +548,7 @@ bool CConsole::AutoComplete(void) for (int j = 0; j < iret; ++j) { - m_vSuggest.push_back(CSuggest(commands[j].String(), COMMAND_COMPLETION_MARKER)); + m_vecSuggest.push_back(ConAutoCompleteSuggest_s(commands[j].String(), COMMAND_COMPLETION_MARKER)); } } else @@ -562,159 +557,173 @@ bool CConsole::AutoComplete(void) } } - if (m_vSuggest.empty()) + if (m_vecSuggest.empty()) { return false; } - m_bSuggestActive = true; + m_autoCompleteActive = true; return true; } //----------------------------------------------------------------------------- // Purpose: resets the auto complete window //----------------------------------------------------------------------------- -void CConsole::ResetAutoComplete(void) +void CConsole::ResetAutoCompleteData(void) { - m_nSuggestPos = PositionMode_t::kPark; - m_bCanAutoComplete = false; - m_bSuggestActive = false; - m_bSuggestMoved = true; - m_vSuggest.clear(); + 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::FindFromPartial(void) +void CConsole::CreateSuggestionsFromPartial(void) { - ResetAutoComplete(); + ResetAutoCompleteData(); ICvar::Iterator iter(g_pCVar); for (iter.SetFirst(); iter.IsValid(); iter.Next()) { - if (m_vSuggest.size() >= con_suggest_limit.GetInt()) + if (m_vecSuggest.size() >= con_suggest_limit.GetInt()) { break; } - const ConCommandBase* pCommandBase = iter.Get(); - if (pCommandBase->IsFlagSet(FCVAR_HIDDEN)) + const ConCommandBase* const commandBase = iter.Get(); + + if (commandBase->IsFlagSet(FCVAR_HIDDEN)) { continue; } - const char* pCommandName = pCommandBase->GetName(); - if (!V_stristr(pCommandName, m_szInputBuf)) + const char* const commandName = commandBase->GetName(); + + if (!V_stristr(commandName, m_inputTextBuf)) { continue; } - if (std::find(m_vSuggest.begin(), m_vSuggest.end(), - pCommandName) == m_vSuggest.end()) + if (std::find(m_vecSuggest.begin(), m_vecSuggest.end(), + commandName) == m_vecSuggest.end()) { - string svValue; + string docString; - if (!pCommandBase->IsCommand()) + if (!commandBase->IsCommand()) { - const ConVar* pConVar = reinterpret_cast(pCommandBase); + const ConVar* conVar = reinterpret_cast(commandBase); - svValue = " = ["; // Assign current value to string if its a ConVar. - svValue.append(pConVar->GetString()); - svValue.append("]"); + docString = " = ["; // Assign current value to string if its a ConVar. + docString.append(conVar->GetString()); + docString.append("]"); } - if (con_suggest_showhelptext.GetBool()) + if (con_suggest_helptext.GetBool()) { - std::function fnAppendDocString = [&](string& svTarget, const char* pszDocString) + std::function fnAppendDocString = [&](string& targetString, const char* toAppend) { - if (VALID_CHARSTAR(pszDocString)) + if (VALID_CHARSTAR(toAppend)) { - svTarget.append(" - \""); - svTarget.append(pszDocString); - svTarget.append("\""); + targetString.append(" - \""); + targetString.append(toAppend); + targetString.append("\""); } }; - fnAppendDocString(svValue, pCommandBase->GetHelpText()); - fnAppendDocString(svValue, pCommandBase->GetUsageText()); + fnAppendDocString(docString, commandBase->GetHelpText()); + fnAppendDocString(docString, commandBase->GetUsageText()); } - m_vSuggest.push_back(CSuggest(pCommandName + svValue, pCommandBase->GetFlags())); + m_vecSuggest.push_back(ConAutoCompleteSuggest_s(commandName + docString, commandBase->GetFlags())); + } + else + { + // Trying to push a duplicate ConCommandBase in the vector; code bug. + Assert(0); } - else { break; } } - std::sort(m_vSuggest.begin(), m_vSuggest.end()); + std::sort(m_vecSuggest.begin(), m_vecSuggest.end()); } //----------------------------------------------------------------------------- // Purpose: processes submitted commands for the main thread -// Input : svCommand - +// Input : inputText - //----------------------------------------------------------------------------- -void CConsole::ProcessCommand(string svCommand) +void CConsole::ProcessCommand(const char* const inputText) { - StringRTrim(svCommand, " "); // Remove trailing white space characters to prevent history duplication. + 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(), svCommand.c_str()); + AddLog(commandColor, "%s] %s\n", Plat_GetProcessUpTime(), commandFormatted.c_str()); - Cbuf_AddText(Cbuf_GetCurrentPlayer(), svCommand.c_str(), cmd_source_t::kCommandSrcCode); - m_nHistoryPos = PositionMode_t::kPark; + Cbuf_AddText(Cbuf_GetCurrentPlayer(), commandFormatted.c_str(), cmd_source_t::kCommandSrcCode); + m_historyPos = ConAutoCompletePos_e::kPark; - AddHistory(svCommand.c_str()); + AddHistory(commandFormatted.c_str()); - m_Logger.ShouldScrollToStart(true); - m_Logger.ShouldScrollToBottom(true); + m_colorTextLogger.ShouldScrollToStart(true); + m_colorTextLogger.ShouldScrollToBottom(true); } //----------------------------------------------------------------------------- -// Purpose: builds the console summary -// Input : svConVar - +// 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::BuildSummary(string svConVar) +void CConsole::BuildSummaryText(const char* const inputText) { - if (!svConVar.empty()) + if (*inputText) { - // Remove trailing space and/or semicolon before we call 'g_pCVar->FindVar(..)'. - StringRTrim(svConVar, " ;", true); - const ConVar* const pConVar = g_pCVar->FindVar(svConVar.c_str()); + string conVarFormatted(inputText); - if (pConVar && !pConVar->IsFlagSet(FCVAR_HIDDEN)) + // 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_szSummary, sizeof(m_szSummary), "(\"%s\", default \"%s\")", pConVar->GetString(), pConVar->GetDefault()); + snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "(\"%s\", default \"%s\")", + conVar->GetString(), conVar->GetDefault()); + return; } } - snprintf(m_szSummary, sizeof(m_szSummary), "%zu history items", m_vHistory.size()); + snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "%zu history items", m_vecHistory.size()); } //----------------------------------------------------------------------------- -// Purpose: builds the selected suggestion for input field +// Purpose: creates the selected suggestion for input field // Input : &suggest - //----------------------------------------------------------------------------- -void CConsole::BuildInputFromSelected(const CSuggest& suggest, string& svInput) +void CConsole::DetermineInputTextFromSelectedSuggestion(const ConAutoCompleteSuggest_s& suggest, string& svInput) { - if (suggest.m_nFlags == COMMAND_COMPLETION_MARKER) + if (suggest.flags == COMMAND_COMPLETION_MARKER) { - svInput = suggest.m_svName + ' '; + svInput = suggest.text + ' '; } else // Remove the default value from ConVar before assigning it to the input buffer. { - svInput = suggest.m_svName.substr(0, suggest.m_svName.find(' ')) + ' '; + svInput = suggest.text.substr(0, suggest.text.find(' ')) + ' '; } } //----------------------------------------------------------------------------- -// Purpose: builds the suggestion panel rect +// Purpose: determines the autocomplete window rect //----------------------------------------------------------------------------- -void CConsole::BuildSuggestPanelRect(void) +void CConsole::DetermineAutoCompleteWindowRect(void) { float flSinglePadding = 0.f; const float flItemHeight = ImGui::GetTextLineHeightWithSpacing() + 1.0f; - if (m_vSuggest.size() > 1) + if (m_vecSuggest.size() > 1) { // Pad with 18 to keep all items in view. flSinglePadding = flItemHeight; @@ -725,8 +734,8 @@ void CConsole::BuildSuggestPanelRect(void) const ImVec2 lastItemRectMin = ImGui::GetItemRectMin(); const ImVec2 lastItemRectSize = ImGui::GetItemRectSize(); - m_ivSuggestWindowPos = lastItemRectMin; - m_ivSuggestWindowPos.y += lastItemRectSize.y; + m_autoCompleteWindowPos = lastItemRectMin; + m_autoCompleteWindowPos.y += lastItemRectSize.y; const float maxWindowWidth = con_autocomplete_window_width.GetFloat(); @@ -739,11 +748,11 @@ void CConsole::BuildSuggestPanelRect(void) const static float minWindowHeight = 37.0f; const float flWindowHeight = flSinglePadding + ImClamp( - static_cast(m_vSuggest.size() * flItemHeight), + static_cast(m_vecSuggest.size() * flItemHeight), minWindowHeight, con_autocomplete_window_height.GetFloat()); - m_ivSuggestWindowSize = ImVec2(flWindowWidth, flWindowHeight); + m_autoCompleteWindowRect = ImVec2(flWindowWidth, flWindowHeight); } //----------------------------------------------------------------------------- @@ -757,13 +766,14 @@ bool CConsole::LoadFlagIcons(void) // Get all flag image resources for displaying flags. for (int i = IDB_PNG3, k = NULL; i <= IDB_PNG32; i++, k++) { - m_vFlagIcons.push_back(MODULERESOURCE(GetModuleResource(i))); - MODULERESOURCE& rFlagIcon = m_vFlagIcons[k]; + m_vecFlagIcons.push_back(MODULERESOURCE(GetModuleResource(i))); + MODULERESOURCE& rFlagIcon = m_vecFlagIcons[k]; ret = LoadTextureBuffer(reinterpret_cast(rFlagIcon.m_pData), // !TODO: Fall-back texture. static_cast(rFlagIcon.m_nSize), &rFlagIcon.m_idIcon, &rFlagIcon.m_nWidth, &rFlagIcon.m_nHeight); IM_ASSERT(ret); + if (!ret) { break; @@ -777,9 +787,9 @@ bool CConsole::LoadFlagIcons(void) // in the future we should build the texture procedurally with use of popcnt. // Input : nFlags - //----------------------------------------------------------------------------- -int CConsole::GetFlagTextureIndex(int nFlags) const +int CConsole::GetFlagTextureIndex(const int flags) const { - switch (nFlags) // All indices for single/dual flag textures. + switch (flags) // All indices for single/dual flag textures. { case FCVAR_DEVELOPMENTONLY: return 9; @@ -826,7 +836,7 @@ int CConsole::GetFlagTextureIndex(int nFlags) const default: // Hit when flag is zero/non-indexed or 3+ bits are set. - int v = __popcnt(nFlags); + const unsigned int v = __popcnt(flags); switch (v) { case 0: @@ -839,17 +849,17 @@ int CConsole::GetFlagTextureIndex(int nFlags) const // and display the appropriate checker texture. bool mul = v > 2; - if (nFlags & FCVAR_DEVELOPMENTONLY) + if (flags & FCVAR_DEVELOPMENTONLY) { return mul ? 4 : 3; } - else if (nFlags & FCVAR_CHEAT) + else if (flags & FCVAR_CHEAT) { return mul ? 6 : 5; } - else if (nFlags & FCVAR_RELEASE && // RELEASE command but no context restriction. - !(nFlags & FCVAR_SERVER_CAN_EXECUTE) && - !(nFlags & FCVAR_CLIENTCMD_CAN_EXECUTE)) + 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; } @@ -888,88 +898,99 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) } case ImGuiInputTextFlags_CallbackHistory: { - if (m_bSuggestActive) + if (m_autoCompleteActive) { - if (iData->EventKey == ImGuiKey_UpArrow && m_nSuggestPos > - 1) + if (iData->EventKey == ImGuiKey_UpArrow && m_suggestPos > - 1) { - m_nSuggestPos--; - m_bSuggestMoved = true; + m_suggestPos--; + m_autoCompletePosMoved = true; } else if (iData->EventKey == ImGuiKey_DownArrow) { - if (m_nSuggestPos < static_cast(m_vSuggest.size()) - 1) + if (m_suggestPos < static_cast(m_vecSuggest.size()) - 1) { - m_nSuggestPos++; - m_bSuggestMoved = true; + m_suggestPos++; + m_autoCompletePosMoved = true; } } } else // Allow user to navigate through the history if suggest panel isn't drawn. { - const ssize_t nPrevHistoryPos = m_nHistoryPos; + const int prevHistoryPos = m_historyPos; + if (iData->EventKey == ImGuiKey_UpArrow) { - if (m_nHistoryPos == PositionMode_t::kPark) + if (m_historyPos == ConAutoCompletePos_e::kPark) { - m_nHistoryPos = static_cast(m_vHistory.size()) - 1; + m_historyPos = static_cast(m_vecHistory.size()) - 1; } - else if (m_nHistoryPos > 0) + else if (m_historyPos > 0) { - m_nHistoryPos--; + m_historyPos--; } } else if (iData->EventKey == ImGuiKey_DownArrow) { - if (m_nHistoryPos != PositionMode_t::kPark) + if (m_historyPos != ConAutoCompletePos_e::kPark) { - if (++m_nHistoryPos >= static_cast(m_vHistory.size())) + if (++m_historyPos >= static_cast(m_vecHistory.size())) { - m_nHistoryPos = PositionMode_t::kPark; + m_historyPos = ConAutoCompletePos_e::kPark; } } } - if (nPrevHistoryPos != m_nHistoryPos) + + if (prevHistoryPos != m_historyPos) { - string svHistory = (m_nHistoryPos >= 0) ? m_vHistory[m_nHistoryPos] : ""; - if (!svHistory.empty()) + string historyText = (m_historyPos >= 0) ? m_vecHistory[m_historyPos] : ""; + + if (!historyText.empty()) { - if (svHistory.find(' ') == string::npos) + if (historyText.find(' ') == string::npos) { - // Append whitespace to previous entered command if absent or no parameters where passed. - svHistory.append(" "); + // 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, svHistory.c_str()); + iData->InsertChars(0, historyText.c_str()); } } - BuildSummary(iData->Buf); + + BuildSummaryText(iData->Buf); break; } case ImGuiInputTextFlags_CallbackAlways: { - m_nInputTextLen = iData->BufTextLen; - if (m_bModifyInput) // User entered a value in the input field. + m_selectedSuggestionTextLen = iData->BufTextLen; + + if (m_inputTextBufModified) // User entered a value in the input field. { - iData->DeleteChars(0, m_nInputTextLen); + iData->DeleteChars(0, m_selectedSuggestionTextLen); - if (!m_svInputConVar.empty()) // User selected a ConVar from the suggestion window, copy it to the buffer. + if (!m_selectedSuggestionText.empty()) // User selected a ConVar from the suggestion window, copy it to the buffer. { - iData->InsertChars(0, m_svInputConVar.c_str()); - m_svInputConVar.clear(); + iData->InsertChars(0, m_selectedSuggestionText.c_str()); + m_selectedSuggestionText.clear(); - m_bCanAutoComplete = true; + m_canAutoComplete = true; m_reclaimFocus = true; } - m_bModifyInput = false; + + m_inputTextBufModified = false; } + break; } case ImGuiInputTextFlags_CallbackCharFilter: { const ImWchar c = iData->EventChar; - if (!m_nInputTextLen) + + if (!m_selectedSuggestionTextLen) { if (c == '~') // Discard tilde character as first input. { @@ -977,6 +998,7 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) return 1; } } + if (c == '`') // Discard back quote character (default console invoke key). { iData->EventChar = 0; @@ -997,17 +1019,18 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) if (iData->BufTextLen) // Attempt to build a summary.. { - BuildSummary(iData->Buf); - m_bCanAutoComplete = true; + BuildSummaryText(iData->Buf); + m_canAutoComplete = true; } else // Reset state and enable history scrolling when buffer is empty. { - ResetAutoComplete(); + ResetAutoCompleteData(); } break; } } + return 0; } @@ -1018,7 +1041,7 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) //----------------------------------------------------------------------------- int CConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* iData) { - CConsole* pConsole = reinterpret_cast(iData->UserData); + CConsole* const pConsole = reinterpret_cast(iData->UserData); return pConsole->TextEditCallback(iData); } @@ -1030,9 +1053,9 @@ int CConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* iData) //----------------------------------------------------------------------------- void CConsole::AddLog(const char* const text, const ImU32 color) { - AUTO_LOCK(m_Mutex); + AUTO_LOCK(m_colorTextLoggerMutex); - m_Logger.InsertText(text, color); + m_colorTextLogger.InsertText(text, color); ClampLogSize(); } @@ -1060,14 +1083,14 @@ void CConsole::AddLog(const ImU32 color, const char* fmt, ...) /*IM_FMTARGS(2)*/ //----------------------------------------------------------------------------- void CConsole::RemoveLog(int nStart, int nEnd) { - AUTO_LOCK(m_Mutex); + AUTO_LOCK(m_colorTextLoggerMutex); - const int nLines = m_Logger.GetTotalLines(); + const int numLines = m_colorTextLogger.GetTotalLines(); - if (nEnd >= nLines) + if (nEnd >= numLines) { // Sanitize for last array elem. - nEnd = (nLines - 1); + nEnd = (numLines - 1); } if (nStart >= nEnd) @@ -1088,13 +1111,13 @@ void CConsole::RemoveLog(int nStart, int nEnd) } // User wants to remove everything. - if (nLines <= (nStart - nEnd)) + if (numLines <= (nStart - nEnd)) { ClearLog(); return; } - m_Logger.RemoveLine(nStart, nEnd); + m_colorTextLogger.RemoveLine(nStart, nEnd); } //----------------------------------------------------------------------------- @@ -1102,8 +1125,8 @@ void CConsole::RemoveLog(int nStart, int nEnd) //----------------------------------------------------------------------------- void CConsole::ClearLog(void) { - AUTO_LOCK(m_Mutex); - m_Logger.RemoveLine(0, (m_Logger.GetTotalLines() - 1)); + AUTO_LOCK(m_colorTextLoggerMutex); + m_colorTextLogger.RemoveLine(0, (m_colorTextLogger.GetTotalLines() - 1)); } //----------------------------------------------------------------------------- @@ -1112,20 +1135,22 @@ void CConsole::ClearLog(void) void CConsole::ClampLogSize(void) { // +1 since the first row is a dummy - const int nMaxLines = con_max_lines.GetInt() + 1; + const int maxLines = con_max_lines.GetInt() + 1; - if (m_Logger.GetTotalLines() > nMaxLines) + if (m_colorTextLogger.GetTotalLines() > maxLines) { - while (m_Logger.GetTotalLines() > nMaxLines) + while (m_colorTextLogger.GetTotalLines() > maxLines) { - m_Logger.RemoveLine(0); - m_nScrollBack++; - m_nSelectBack++; + m_colorTextLogger.RemoveLine(0); + + m_scrollBackAmount++; + m_selectBackAmount++; } - m_Logger.MoveSelection(m_nSelectBack, false); - m_Logger.MoveCursor(m_nSelectBack, false); - m_nSelectBack = 0; + m_colorTextLogger.MoveSelection(m_selectBackAmount, false); + m_colorTextLogger.MoveCursor(m_selectBackAmount, false); + + m_selectBackAmount = 0; } } @@ -1138,16 +1163,16 @@ 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_vHistory.size(); i-- > 0;) + for (size_t i = m_vecHistory.size(); i-- > 0;) { - if (m_vHistory[i].compare(command) == 0) + if (m_vecHistory[i].compare(command) == 0) { - m_vHistory.erase(m_vHistory.begin() + i); + m_vecHistory.erase(m_vecHistory.begin() + i); break; } } - m_vHistory.push_back(command); + m_vecHistory.push_back(command); ClampHistorySize(); } @@ -1157,7 +1182,7 @@ void CConsole::AddHistory(const char* const command) //----------------------------------------------------------------------------- const vector& CConsole::GetHistory(void) const { - return m_vHistory; + return m_vecHistory; } //----------------------------------------------------------------------------- @@ -1165,8 +1190,8 @@ const vector& CConsole::GetHistory(void) const //----------------------------------------------------------------------------- void CConsole::ClearHistory(void) { - m_vHistory.clear(); - BuildSummary(); + m_vecHistory.clear(); + BuildSummaryText(""); } //----------------------------------------------------------------------------- @@ -1174,9 +1199,9 @@ void CConsole::ClearHistory(void) //----------------------------------------------------------------------------- void CConsole::ClampHistorySize(void) { - while (m_vHistory.size() > con_max_history.GetInt()) + while (m_vecHistory.size() > con_max_history.GetInt()) { - m_vHistory.erase(m_vHistory.begin()); + m_vecHistory.erase(m_vecHistory.begin()); } } @@ -1212,8 +1237,8 @@ void CConsole::RemoveLine_f(const CCommand& args) return; } - int start = atoi(args[1]); - int end = atoi(args[2]); + const int start = atoi(args[1]); + const int end = atoi(args[2]); g_Console.RemoveLog(start, end); } diff --git a/r5dev/gameui/IConsole.h b/r5dev/gameui/IConsole.h index 720a9ea3..c71ad20d 100644 --- a/r5dev/gameui/IConsole.h +++ b/r5dev/gameui/IConsole.h @@ -15,28 +15,29 @@ public: virtual ~CConsole(void); virtual bool Init(void); + virtual void Shutdown(void); virtual void RunFrame(void); virtual bool DrawSurface(void); private: - void OptionsPanel(void); - void SuggestPanel(void); + void DrawOptionsPanel(void); + void DrawAutoCompletePanel(void); - bool AutoComplete(void); - void ResetAutoComplete(void); + bool RunAutoComplete(void); + void ResetAutoCompleteData(void); - void FindFromPartial(void); - void ProcessCommand(string svCommand); + void CreateSuggestionsFromPartial(void); + void ProcessCommand(const char* const inputText); - void BuildSummary(string svConVar = ""); + void BuildSummaryText(const char* const inputText); - struct CSuggest; - void BuildInputFromSelected(const CSuggest& suggest, string& svInput); - void BuildSuggestPanelRect(void); + struct ConAutoCompleteSuggest_s; + void DetermineInputTextFromSelectedSuggestion(const ConAutoCompleteSuggest_s& suggest, string& svInput); + void DetermineAutoCompleteWindowRect(void); bool LoadFlagIcons(void); - int GetFlagTextureIndex(int nFlags) const; + int GetFlagTextureIndex(const int flags) const; int TextEditCallback(ImGuiInputTextCallbackData* pData); static int TextEditCallbackStub(ImGuiInputTextCallbackData* pData); @@ -66,70 +67,106 @@ private: // Internals. void ClampHistorySize(void); private: - enum PositionMode_t + enum ConAutoCompletePos_e { // Park means the position is out of screen. kPark = -1, - kFirst, }; - struct CSuggest + struct ConAutoCompleteSuggest_s { - CSuggest(const string& svName, int nFlags) + ConAutoCompleteSuggest_s(const string& inText, const int inFlags) { - m_svName = svName; - m_nFlags = nFlags; + text = inText; + flags = inFlags; } bool operator==(const string& a) const { - return m_svName.compare(a) == 0; + return text.compare(a) == 0; } - bool operator<(const CSuggest& a) const + bool operator<(const ConAutoCompleteSuggest_s& a) const { - return m_svName < a.m_svName; + return text < a.text; } - string m_svName; - int m_nFlags; + string text; + int flags; }; private: /////////////////////////////////////////////////////////////////////////// - const char* m_pszLoggingLabel; - char m_szInputBuf[512]; - char m_szSummary[256]; - char m_szWindowLabel[128]; + const char* m_loggerLabel; + char m_inputTextBuf[512]; + char m_summaryTextBuf[256]; - string m_svInputConVar; - ssize_t m_nHistoryPos; - ssize_t m_nSuggestPos; - int m_nScrollBack; - int m_nSelectBack; - int m_nInputTextLen; - float m_flScrollX; - float m_flScrollY; + // The selected ConVar from the suggestions in the autocomplete window gets + // copied into this buffer. + string m_selectedSuggestionText; + int m_selectedSuggestionTextLen; - bool m_bCopyToClipBoard; - bool m_bModifyInput; + // The positions in the history buffer; when there is nothing in the input + // text field, the arrow keys would instead iterate over the previously + // submitted commands and show the currently selected one right in the + // input text field. + int m_historyPos; - bool m_bCanAutoComplete; - bool m_bSuggestActive; - bool m_bSuggestMoved; - bool m_bSuggestUpdate; + // The position in the autocomplete window; this dictates the current + // (highlighted) suggestion, and copies that one into m_selectedSuggestion + // once selected. + int m_suggestPos; - vector m_vSuggest; - vector m_vFlagIcons; - vector m_vHistory; + // Scroll and select back amount; if text lines are getting removed to + // clamp the size to the maximum allowed, we need to scroll back (and + // if text has been selected, select back this amount) to prevent the + // view or selection from drifting. + int m_scrollBackAmount; + int m_selectBackAmount; - ImVec2 m_ivSuggestWindowPos; - ImVec2 m_ivSuggestWindowSize; + // Scroll position of the last drawn frame, after any scroll back has been + // applied, this is used as a base for scrolling back when entries are + // getting removed. + ImVec2 m_lastFrameScrollPos; - CTextLogger m_Logger; - mutable CThreadFastMutex m_Mutex; + // Set when the input text has been modified, used to rebuild suggestions + // shown in the autocomplete window. + bool m_inputTextBufModified; - ImGuiInputTextFlags m_nInputFlags; - ImGuiWindowFlags m_nSuggestFlags; - ImGuiWindowFlags m_nLoggingFlags; + // Determines whether we can autocomplete and build a list of suggestions, + // e.g. when you type "map " (with a trailing white space ' '), and "map" + // happens to be a ConCommand with an autocomplete function, this gets set + // and the autocomplete function of that ConCommand will be called to + // create a list of suggestions to be shown in the autocomplete window. + // This member is always false when the input text is empty. + bool m_canAutoComplete; + + // Whether the autocomplete window is active. If this is set, the arrow up + // and down keys will be used for the auto complete window instead of the + // history (previously submitted commands) scroller. + bool m_autoCompleteActive; + + // If the position in the autocomplete window had moved, this var will be + // set. This is used to check if we need to adjust the scroll position in + // the autocomplete window to keep the current selection visible. + bool m_autoCompletePosMoved; + + // The position and rect of the autocomplete window, the pos is set to that + // of the input text field + an offset to move it under the item. + ImVec2 m_autoCompleteWindowPos; + ImVec2 m_autoCompleteWindowRect; + + vector m_vecSuggest; + vector m_vecFlagIcons; + vector m_vecHistory; + + // The color logger in which text gets added, note that the mutex lock + // should always be acquired when using this as this is accessed from + // multiple threads! + CTextLogger m_colorTextLogger; + mutable CThreadFastMutex m_colorTextLoggerMutex; + + ImGuiInputTextFlags m_inputTextFieldFlags; + ImGuiWindowFlags m_autoCompleteWindowFlags; + ImGuiWindowFlags m_colorLoggerWindowFlags; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/gameui/imgui_surface.h b/r5dev/gameui/imgui_surface.h index b7d85efd..9220ba9e 100644 --- a/r5dev/gameui/imgui_surface.h +++ b/r5dev/gameui/imgui_surface.h @@ -10,7 +10,9 @@ class CImguiSurface public: CImguiSurface(); virtual ~CImguiSurface() { }; + virtual bool Init() = 0; + virtual void Shutdown() = 0; virtual void Animate(); virtual void RunFrame() = 0;