From 301cb6577c50d62cae7ef16ae48a447787a020f2 Mon Sep 17 00:00:00 2001 From: Andrej Redeky Date: Fri, 2 Apr 2021 13:11:33 +0200 Subject: [PATCH 1/3] Minor additions to UI - Changed some ImGui::Text calls to ImGui::TextUnformatted to not unnecessarily invoke snprintf - Added Reset Changes button to Bindings widget [new function is also used by #529 ] - Added new helper widget for unsaved changes modal [used by #529 ] - Made existing widgets a bit better [rest of additions are coupled inside #529 as that would not compile on its own in this state] --- src/overlay/widgets/Bindings.cpp | 18 ++++++++ src/overlay/widgets/Bindings.h | 1 + src/overlay/widgets/HelperWidgets.cpp | 64 +++++++++++++++++++++++++-- src/overlay/widgets/HelperWidgets.h | 6 ++- src/overlay/widgets/TweakDBEditor.cpp | 58 ++++++++++++------------ 5 files changed, 113 insertions(+), 34 deletions(-) diff --git a/src/overlay/widgets/Bindings.cpp b/src/overlay/widgets/Bindings.cpp index 9ad8ec6f..1eb36e92 100644 --- a/src/overlay/widgets/Bindings.cpp +++ b/src/overlay/widgets/Bindings.cpp @@ -32,6 +32,9 @@ void Bindings::Update() ImGui::SameLine(); if (ImGui::Button("Save")) Save(); + ImGui::SameLine(); + if (ImGui::Button("Reset changes")) + ResetChanges(); ImGui::Spacing(); @@ -84,3 +87,18 @@ void Bindings::Save() m_vkBindInfos = m_bindings.InitializeMods(m_vm.GetBinds()); } + +void Bindings::ResetChanges() +{ + for (auto& vkBindInfo : m_vkBindInfos) + { + if (vkBindInfo.CodeBind == vkBindInfo.SavedCodeBind) + continue; + + if (vkBindInfo.CodeBind) + m_bindings.UnBind(vkBindInfo.CodeBind); + if (vkBindInfo.SavedCodeBind) + m_bindings.Bind(vkBindInfo.SavedCodeBind, vkBindInfo.Bind); + vkBindInfo.CodeBind = vkBindInfo.SavedCodeBind; + } +} diff --git a/src/overlay/widgets/Bindings.h b/src/overlay/widgets/Bindings.h index 46053868..30b61c64 100644 --- a/src/overlay/widgets/Bindings.h +++ b/src/overlay/widgets/Bindings.h @@ -16,6 +16,7 @@ struct Bindings : Widget void Load(); void Save(); + void ResetChanges(); private: std::vector m_vkBindInfos{ }; diff --git a/src/overlay/widgets/HelperWidgets.cpp b/src/overlay/widgets/HelperWidgets.cpp index 1b663500..66f09855 100644 --- a/src/overlay/widgets/HelperWidgets.cpp +++ b/src/overlay/widgets/HelperWidgets.cpp @@ -46,9 +46,12 @@ namespace HelperWidgets std::string label { aVKBindInfo.Bind.Description + ':' }; if (aVKBindInfo.Bind.IsHotkey()) label.insert(0, "[HK] "); // insert [HK] prefix for hotkeys so user knows this input can be assigned up to 4-key combo + + ImGui::AlignTextToFramePadding(); + ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); ImGui::PushID(&aVKBindInfo.Bind.Description); // ensure we have unique ID by using pointer to Description, is OK, pointer will not be used inside ImGui :P - ImGui::Text(label.c_str()); + ImGui::TextUnformatted(label.c_str()); ImGui::PopID(); ImGui::PopStyleColor(); @@ -84,19 +87,72 @@ namespace HelperWidgets } } - void BoolWidget(const std::string& label, bool& current, bool saved) + bool BoolWidget(const std::string& label, bool& current, bool saved, float offset_x) { ImVec4 curTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); if (current != saved) curTextColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); - + + ImGui::AlignTextToFramePadding(); + + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offset_x); + ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); - ImGui::Text(label.c_str()); + ImGui::TextUnformatted(label.c_str()); ImGui::PopStyleColor(); ImGui::SameLine(); ImGui::Checkbox(("##" + label).c_str(), ¤t); + + return (current != saved); } + int32_t UnsavedChangesPopup(bool& aFirstTime, bool aMadeChanges, TUCHPSave aSaveCB, TUCHPLoad aLoadCB) + { + if (aMadeChanges) + { + int32_t res = 0; + if (aFirstTime) + { + ImGui::OpenPopup("Unsaved changes"); + aFirstTime = false; + } + + if (ImGui::BeginPopupModal("Unsaved changes", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + const auto shorterTextSz { ImGui::CalcTextSize("You have some unsaved changes.").x }; + const auto longerTextSz { ImGui::CalcTextSize("Do you wish to apply them or discard them?").x }; + const auto diffTextSz { longerTextSz - shorterTextSz }; + + ImGui::SetCursorPosX(diffTextSz / 2); + ImGui::TextUnformatted("You have some unsaved changes."); + ImGui::TextUnformatted("Do you wish to apply them or discard them?"); + ImGui::Separator(); + + const auto buttonWidth { (longerTextSz - ImGui::GetStyle().ItemSpacing.x) / 2 }; + + if (ImGui::Button("Apply", ImVec2(buttonWidth, 0))) + { + aSaveCB(); + res = 1; + aFirstTime = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Discard", ImVec2(buttonWidth, 0))) + { + aLoadCB(); + res = -1; + aFirstTime = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SetItemDefaultFocus(); + + ImGui::EndPopup(); + } + return res; + } + return 1; // no changes, same as if we were to Apply + } } diff --git a/src/overlay/widgets/HelperWidgets.h b/src/overlay/widgets/HelperWidgets.h index 4970615e..cd163bc3 100644 --- a/src/overlay/widgets/HelperWidgets.h +++ b/src/overlay/widgets/HelperWidgets.h @@ -6,5 +6,9 @@ namespace HelperWidgets { WidgetID ToolbarWidget(); void BindWidget(VKBindInfo& aVKBindInfo, const std::string& acId); -void BoolWidget(const std::string& label, bool& current, bool saved); +bool BoolWidget(const std::string& label, bool& current, bool saved, float offset_x = 0.0f); + +using TUCHPSave = std::function; +using TUCHPLoad = std::function; +int32_t UnsavedChangesPopup(bool& aFirstTime, bool aMadeChanges, TUCHPSave aSaveCB, TUCHPLoad aLoadCB); } \ No newline at end of file diff --git a/src/overlay/widgets/TweakDBEditor.cpp b/src/overlay/widgets/TweakDBEditor.cpp index b7543379..7917d360 100644 --- a/src/overlay/widgets/TweakDBEditor.cpp +++ b/src/overlay/widgets/TweakDBEditor.cpp @@ -375,7 +375,7 @@ void TweakDBEditor::Update() // LuaVM is initialized after TweakDB, let's wait for it if (!m_vm.IsInitialized()) { - ImGui::Text("TweakDB is not initialized yet"); + ImGui::TextUnformatted("TweakDB is not initialized yet"); return; } @@ -1116,22 +1116,22 @@ bool TweakDBEditor::DrawFlatQuaternion(RED4ext::TweakDBID aDBID, RED4ext::CStack int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; - ImGui::Text("I"); + ImGui::TextUnformatted("I"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); bool valueChanged = ImGui::InputFloat("##I", &i, 0.0f, 0.0f, "%f", flags); - ImGui::Text("J"); + ImGui::TextUnformatted("J"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##J", &j, 0.0f, 0.0f, "%f", flags); - ImGui::Text("K"); + ImGui::TextUnformatted("K"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##K", &k, 0.0f, 0.0f, "%f", flags); - ImGui::Text("R"); + ImGui::TextUnformatted("R"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##R", &r, 0.0f, 0.0f, "%f", flags); @@ -1169,17 +1169,17 @@ bool TweakDBEditor::DrawFlatEulerAngles(RED4ext::TweakDBID aDBID, RED4ext::CStac int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; - ImGui::Text("Roll "); + ImGui::TextUnformatted("Roll "); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); bool valueChanged = ImGui::InputFloat("##Roll", &roll, 0.0f, 0.0f, "%f", flags); - ImGui::Text("Pitch"); + ImGui::TextUnformatted("Pitch"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##Pitch", &pitch, 0.0f, 0.0f, "%f", flags); - ImGui::Text("Yaw "); + ImGui::TextUnformatted("Yaw "); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##Yaw", &yaw, 0.0f, 0.0f, "%f", flags); @@ -1216,17 +1216,17 @@ bool TweakDBEditor::DrawFlatVector3(RED4ext::TweakDBID aDBID, RED4ext::CStackTyp int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; - ImGui::Text("X"); + ImGui::TextUnformatted("X"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); bool valueChanged = ImGui::InputFloat("##X", &x, 0.0f, 0.0f, "%f", flags); - ImGui::Text("Y"); + ImGui::TextUnformatted("Y"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##Y", &y, 0.0f, 0.0f, "%f", flags); - ImGui::Text("Z"); + ImGui::TextUnformatted("Z"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##Z", &z, 0.0f, 0.0f, "%f", flags); @@ -1262,12 +1262,12 @@ bool TweakDBEditor::DrawFlatVector2(RED4ext::TweakDBID aDBID, RED4ext::CStackTyp int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; - ImGui::Text("X"); + ImGui::TextUnformatted("X"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); bool valueChanged = ImGui::InputFloat("##X", &x, 0.0f, 0.0f, "%f", flags); - ImGui::Text("Y"); + ImGui::TextUnformatted("Y"); ImGui::SameLine(); ImGui::SetNextItemWidth(-1); valueChanged |= ImGui::InputFloat("##Y", &y, 0.0f, 0.0f, "%f", flags); @@ -1304,7 +1304,7 @@ bool TweakDBEditor::DrawFlatColor(RED4ext::TweakDBID aDBID, RED4ext::CStackType& rgba[3] = pColor->Alpha / 255.0f; aReadOnly = true; - ImGui::Text("'Color' is not supported yet"); + ImGui::TextUnformatted("'Color' is not supported yet"); ImGui::SameLine(); int32_t flags = aReadOnly ? ImGuiColorEditFlags_NoInputs : ImGuiColorEditFlags_None; @@ -1342,8 +1342,8 @@ bool TweakDBEditor::DrawFlatLocKeyWrapper(RED4ext::TweakDBID aDBID, RED4ext::CSt { auto* pLocKey = static_cast(aStackType.value); - ImGui::Text("This is a LocalizationKey"); - ImGui::Text("Game.GetLocalizedTextByKey(...)"); + ImGui::TextUnformatted("This is a LocalizationKey"); + ImGui::TextUnformatted("Game.GetLocalizedTextByKey(...)"); uint64_t key = pLocKey->unk00; int32_t flags = aReadOnly ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_EnterReturnsTrue; @@ -1505,8 +1505,8 @@ bool TweakDBEditor::DrawFlatCName(RED4ext::TweakDBID aDBID, RED4ext::CStackType& { auto* pCName = static_cast(aStackType.value); - ImGui::Text("This is not just a string."); - ImGui::Text("Game is expecting specific values."); + ImGui::TextUnformatted("This is not just a string."); + ImGui::TextUnformatted("Game is expecting specific values."); // Is it worth it to implement a dropdown like DrawTweakDBID? RED4ext::CName newCName; @@ -1743,7 +1743,7 @@ void TweakDBEditor::DrawRecordsTab() ImGui::TableNextColumn(); if (flat.m_isMissing) { - ImGui::Text("ERROR_FLAT_NOT_FOUND"); + ImGui::TextUnformatted("ERROR_FLAT_NOT_FOUND"); } else { @@ -1860,7 +1860,7 @@ void TweakDBEditor::DrawFlatsTab() ImGui::TableNextColumn(); if (flat.m_isMissing) { - ImGui::Text("ERROR_FLAT_NOT_FOUND"); + ImGui::TextUnformatted("ERROR_FLAT_NOT_FOUND"); } else { @@ -1884,13 +1884,13 @@ void TweakDBEditor::DrawAdvancedTab() if (CDPRTweakDBMetadata::Get()->IsInitialized()) { ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00FF00); - ImGui::Text("'tweakdb.str' is loaded!"); + ImGui::TextUnformatted("'tweakdb.str' is loaded!"); ImGui::PopStyleColor(); } else { ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF); - ImGui::Text("'tweakdb.str' is not loaded."); + ImGui::TextUnformatted("'tweakdb.str' is not loaded."); ImGui::PopStyleColor(); ImGui::TreePush(); ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32_BLACK_TRANS); @@ -1898,8 +1898,8 @@ void TweakDBEditor::DrawAdvancedTab() char pLink[] = "https://www.cyberpunk.net/en/modding-support"; ImGui::InputText("##cdprLink", pLink, sizeof(pLink) - 1, ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleColor(2); - ImGui::Text("1) Download and unpack 'Metadata'"); - ImGui::Text("2) Copy 'tweakdb.str' to 'plugins\\cyber_engine_tweaks\\tweakdb.str'"); + ImGui::TextUnformatted("1) Download and unpack 'Metadata'"); + ImGui::TextUnformatted("2) Copy 'tweakdb.str' to 'plugins\\cyber_engine_tweaks\\tweakdb.str'"); std::string cetDir = CET::Get().GetPaths().CETRoot().string(); ImGui::Text("Full path: %s", cetDir.c_str()); if (ImGui::Button("3) Load tweakdb.str")) @@ -1916,13 +1916,13 @@ void TweakDBEditor::DrawAdvancedTab() if (ResourcesList::Get()->IsInitialized()) { ImGui::PushStyleColor(ImGuiCol_Text, 0xFF00FF00); - ImGui::Text("'archivehashes.txt' is loaded!"); + ImGui::TextUnformatted("'archivehashes.txt' is loaded!"); ImGui::PopStyleColor(); } else { ImGui::PushStyleColor(ImGuiCol_Text, 0xFF0000FF); - ImGui::Text("'archivehashes.txt' is not loaded."); + ImGui::TextUnformatted("'archivehashes.txt' is not loaded."); ImGui::PopStyleColor(); ImGui::TreePush(); ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32_BLACK_TRANS); @@ -1930,8 +1930,8 @@ void TweakDBEditor::DrawAdvancedTab() char pLink[] = "https://github.com/WolvenKit/Wolvenkit/raw/master/WolvenKit.Common/Resources/archivehashes.zip"; ImGui::InputText("##wkitLink", pLink, sizeof(pLink) - 1, ImGuiInputTextFlags_ReadOnly); ImGui::PopStyleColor(2); - ImGui::Text("1) Download and unpack 'archivehashes.zip'"); - ImGui::Text("2) Copy 'archivehashes.txt' to 'plugins\\cyber_engine_tweaks\\archivehashes.txt'"); + ImGui::TextUnformatted("1) Download and unpack 'archivehashes.zip'"); + ImGui::TextUnformatted("2) Copy 'archivehashes.txt' to 'plugins\\cyber_engine_tweaks\\archivehashes.txt'"); std::string cetDir = CET::Get().GetPaths().CETRoot().string(); ImGui::Text("Full path: %s", cetDir.c_str()); if (ImGui::Button("3) Load archivehashes.txt")) @@ -2040,7 +2040,7 @@ void TweakDBEditor::DrawAdvancedTab() statusTimer = 0.0f; } } - ImGui::Text(status); + ImGui::TextUnformatted(status); } ImGui::EndChild(); } From 47c264435b873d1f0d282d9c6305b55b668bf402 Mon Sep 17 00:00:00 2001 From: Andrej Redeky Date: Fri, 2 Apr 2021 13:36:13 +0200 Subject: [PATCH 2/3] Code style --- src/overlay/widgets/HelperWidgets.cpp | 12 ++++++------ src/overlay/widgets/HelperWidgets.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/overlay/widgets/HelperWidgets.cpp b/src/overlay/widgets/HelperWidgets.cpp index 66f09855..08d4d2a8 100644 --- a/src/overlay/widgets/HelperWidgets.cpp +++ b/src/overlay/widgets/HelperWidgets.cpp @@ -87,25 +87,25 @@ namespace HelperWidgets } } - bool BoolWidget(const std::string& label, bool& current, bool saved, float offset_x) + bool BoolWidget(const std::string& aLabel, bool& aCurrent, bool aSaved, float aOffsetX) { ImVec4 curTextColor = ImGui::GetStyleColorVec4(ImGuiCol_Text); - if (current != saved) + if (aCurrent != aSaved) curTextColor = ImVec4(1.0f, 1.0f, 0.0f, 1.0f); ImGui::AlignTextToFramePadding(); - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offset_x); + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + aOffsetX); ImGui::PushStyleColor(ImGuiCol_Text, curTextColor); - ImGui::TextUnformatted(label.c_str()); + ImGui::TextUnformatted(aLabel.c_str()); ImGui::PopStyleColor(); ImGui::SameLine(); - ImGui::Checkbox(("##" + label).c_str(), ¤t); + ImGui::Checkbox(("##" + aLabel).c_str(), &aCurrent); - return (current != saved); + return (aCurrent != aSaved); } int32_t UnsavedChangesPopup(bool& aFirstTime, bool aMadeChanges, TUCHPSave aSaveCB, TUCHPLoad aLoadCB) diff --git a/src/overlay/widgets/HelperWidgets.h b/src/overlay/widgets/HelperWidgets.h index cd163bc3..63a35a7e 100644 --- a/src/overlay/widgets/HelperWidgets.h +++ b/src/overlay/widgets/HelperWidgets.h @@ -6,7 +6,7 @@ namespace HelperWidgets { WidgetID ToolbarWidget(); void BindWidget(VKBindInfo& aVKBindInfo, const std::string& acId); -bool BoolWidget(const std::string& label, bool& current, bool saved, float offset_x = 0.0f); +bool BoolWidget(const std::string& aLabel, bool& aCurrent, bool aSaved, float aOffsetX = 0.0f); using TUCHPSave = std::function; using TUCHPLoad = std::function; From 428a5b821bf9e92dbce7484394a840c350ee7703 Mon Sep 17 00:00:00 2001 From: Andrej Redeky Date: Fri, 2 Apr 2021 14:05:34 +0200 Subject: [PATCH 3/3] Change definition for ImGui Text Lua binding Right now, we really expose only TextUnformatted... So let's avoid extra overhead of snprintf when it is not really required in the end in current implementation. --- src/sol_imgui/sol_imgui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sol_imgui/sol_imgui.h b/src/sol_imgui/sol_imgui.h index 38082959..5e42f32b 100644 --- a/src/sol_imgui/sol_imgui.h +++ b/src/sol_imgui/sol_imgui.h @@ -164,7 +164,7 @@ namespace sol_ImGui // Widgets: Text inline void TextUnformatted(const std::string& text) { ImGui::TextUnformatted(text.c_str()); } - inline void Text(const std::string& text) { ImGui::Text(text.c_str()); } + inline void Text(const std::string& text) { ImGui::TextUnformatted(text.c_str()); } // TODO - make this proper call to ImGui::Text, allowing real formatting! inline void TextColored(float colR, float colG, float colB, float colA, const std::string& text) { ImGui::TextColored({ colR, colG, colB, colA }, text.c_str()); } inline void TextDisabled(const std::string& text) { ImGui::TextDisabled(text.c_str()); } inline void TextWrapped(const std::string text) { ImGui::TextWrapped(text.c_str()); }