From 6377fe4d76467ac4f2ec29ec2f20c24cded6a8ab Mon Sep 17 00:00:00 2001 From: narknon <73571427+narknon@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:25:49 -0400 Subject: [PATCH] Begin adding datatable support to live view --- UE4SS/include/GUI/LiveView.hpp | 2 + UE4SS/src/GUI/LiveView.cpp | 263 +++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) diff --git a/UE4SS/include/GUI/LiveView.hpp b/UE4SS/include/GUI/LiveView.hpp index 1929cf773..afbe4d0d0 100644 --- a/UE4SS/include/GUI/LiveView.hpp +++ b/UE4SS/include/GUI/LiveView.hpp @@ -89,6 +89,7 @@ namespace RC::GUI UObject* m_currently_opened_tree_node{}; std::string m_current_property_value_buffer{}; int64_t m_current_enum_value_buffer{}; + unsigned char* m_current_dt_row_value_buffer{}; float m_top_size{300.0f}; float m_bottom_size{0.0f}; UFunctionCallerWidget* m_function_caller_widget{}; @@ -161,6 +162,7 @@ namespace RC::GUI auto render_info_panel_as_property(FProperty*) -> void; auto render_bottom_panel() -> void; auto render_enum() -> void; + auto render_datatable() -> void; auto render_properties() -> void; auto render_object_sub_tree_hierarchy(UObject* object) -> void; auto render_struct_sub_tree_hierarchy(UStruct* ustruct) -> void; diff --git a/UE4SS/src/GUI/LiveView.cpp b/UE4SS/src/GUI/LiveView.cpp index 70cbaec79..3df8ad3a1 100644 --- a/UE4SS/src/GUI/LiveView.cpp +++ b/UE4SS/src/GUI/LiveView.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -2299,6 +2300,263 @@ namespace RC::GUI ImGui::EndTable(); // Enum Table } + auto LiveView::render_datatable() -> void + { + const auto currently_selected_object = get_selected_object(); + if (!currently_selected_object.first || !currently_selected_object.second) + { + return; + } + + auto udatatatble = static_cast(currently_selected_object.second); + auto rowmap = udatatatble->GetRowMap(); + auto dtstruct = udatatatble->GetRowStruct(); + std::string plus = "+"; + std::string minus = "-"; + int32_t index = -1; + + if (!ImGui::BeginTable("Enum", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable)) + { + return; + } + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Struct"); + ImGui::TableSetupColumn("Add/Remove"); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); + + for (const auto row : rowmap) + { + auto row_name = row.Key.ToString(); + + ImGui::TableNextRow(); + bool open_edit_name_popup{}; + bool open_edit_value_popup{}; + bool open_add_name_popup{}; + ++index; + + ImGui::TableNextColumn(); + ImGui::Text("%S", row_name.c_str()); + if (ImGui::BeginPopupContextItem(to_string(std::format(STR("context-menu-{}"), row_name)).c_str())) + { + if (ImGui::MenuItem("Copy name")) + { + ImGui::SetClipboardText(to_string(row_name).c_str()); + } + if (ImGui::MenuItem("Edit name")) + { + open_edit_name_popup = true; + m_modal_edit_property_value_is_open = true; + } + ImGui::EndPopup(); + } + + ImGui::TableNextColumn(); + ImGui::Text("%lld", row.Value); + if (ImGui::BeginPopupContextItem(to_string(std::format(STR("context-menu-{}"), row_name)).c_str())) + { + if (ImGui::MenuItem("Copy value")) + { + /*ImGui::SetClipboardText(std::to_string(row.Value));*/ + } + if (ImGui::MenuItem("Edit value")) + { + open_edit_value_popup = true; + m_modal_edit_property_value_is_open = true; + } + ImGui::EndPopup(); + } + + ImGui::TableNextColumn(); + ImGui::PushID(to_string(std::format(STR("button_add_{}"), row_name)).c_str()); + if (ImGui::Button("+")) + { + open_add_name_popup = true; + m_modal_edit_property_value_is_open = true; + } + ImGui::PopID(); + ImGui::SameLine(); + ImGui::PushID(to_string(std::format(STR("button_remove_{}"), row_name)).c_str()); + if (ImGui::Button("-")) + { + udatatatble->RemoveRow(FName(row_name)); + } + ImGui::PopID(); + + std::string edit_dt_row_name = to_string(std::format(STR("Edit DT row name for: {}"), row.Key.ToString())); + + std::string edit_dt_row_struct = to_string(std::format(STR("Edit DT row struct for: {}"), row.Key.ToString())); + + std::string add_dt_row = to_string(std::format(STR("Enter new DT row name after: {}"), row.Key.ToString())); + + if (open_edit_name_popup) + { + ImGui::OpenPopup(edit_dt_row_name.c_str()); + if (!m_modal_edit_property_value_opened_this_frame) + { + m_modal_edit_property_value_opened_this_frame = true; + m_current_property_value_buffer = to_string(row_name); + } + } + + if (open_edit_value_popup) + { + ImGui::OpenPopup(edit_dt_row_struct.c_str()); + if (!m_modal_edit_property_value_opened_this_frame) + { + m_modal_edit_property_value_opened_this_frame = true; + m_current_dt_row_value_buffer = row.Value; + } + } + + if (open_add_name_popup) + { + ImGui::OpenPopup(add_dt_row.c_str()); + if (!m_modal_edit_property_value_opened_this_frame) + { + m_modal_edit_property_value_opened_this_frame = true; + m_current_property_value_buffer = to_string(row_name); + } + } + + /** + * + * ImGui Popup Modal for editing the names in the UEnum names array. + * + */ + if (ImGui::BeginPopupModal(edit_dt_row_name.c_str(), &m_modal_edit_property_value_is_open)) + { + ImGui::Text("Edit the DT row's name."); + ImGui::Text("The game could crash if the new name is invalid or if the old name or value is expected to be used elsewhere."); + ImGui::Text("The game may not use this value without additional patches."); + ImGui::PushItemWidth(-1.0f); + ImGui::InputText("##CurrentNameValue", &m_current_property_value_buffer); + if (ImGui::Button("Apply")) + { + FOutputDevice placeholder_device{}; + StringType new_name = to_wstring(m_current_property_value_buffer); + FName new_key = FName(new_name, FNAME_Add); + /*uenum->EditNameAt(index, new_key); + if (uenum->GetEnumNames()[index].Key.ToString() != new_name) + { + m_modal_edit_property_value_error_unable_to_edit = true; + ImGui::OpenPopup("UnableToSetNewEnumNameError"); + } + else + { + ImGui::CloseCurrentPopup(); + }*/ + } + + if (ImGui::BeginPopupModal("UnableToSetNewEnumNameError", + &m_modal_edit_property_value_error_unable_to_edit, + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Was unable to set new name."); + ImGui::EndPopup(); + } + + ImGui::EndPopup(); + } + + /** + * + * ImGui Popup Modal for editing the values in the UEnum names array. + * + */ + if (ImGui::BeginPopupModal(edit_dt_row_struct.c_str(), &m_modal_edit_property_value_is_open)) + { + ImGui::Text("Edit the enumerator's value."); + ImGui::Text("The game could crash if the new value is invalid or if the old name or value is expected to be used elsewhere."); + ImGui::Text("The game may not use this value without additional patches."); + ImGui::PushItemWidth(-1.0f); + ImGuiDataType_ imgui_data_type = Version::IsBelow(4, 15) ? ImGuiDataType_U8 : ImGuiDataType_S64; + ImGui::InputScalar("##CurrentNameValue", imgui_data_type, &m_current_enum_value_buffer); + if (ImGui::Button("Apply")) + { + FOutputDevice placeholder_device{}; + unsigned char* new_value = m_current_dt_row_value_buffer; + /*row->EditValueAt(index, new_value); + + if (uenum->GetEnumNames()[index].Value != new_value) + { + m_modal_edit_property_value_error_unable_to_edit = true; + ImGui::OpenPopup("UnableToSetNewEnumValueError"); + } + else + { + ImGui::CloseCurrentPopup(); + }*/ + m_current_enum_value_buffer = {}; + } + + if (ImGui::BeginPopupModal("UnableToSetNewEnumValueError", + &m_modal_edit_property_value_error_unable_to_edit, + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Was unable to set new value."); + ImGui::EndPopup(); + } + + ImGui::EndPopup(); + } + + /** + * + * ImGui Popup Modal for adding new enumerators to the UEnum names array. + * + */ + if (ImGui::BeginPopupModal(add_dt_row.c_str(), &m_modal_edit_property_value_is_open)) + { + + ImGui::Text("Enter the name of the new row at the index of the selected row."); + ImGui::Text("The game could crash if the new name is invalid or if the old name or value is expected to be used elsewhere."); + ImGui::Text("The game may not use this value without additional patches."); + ImGui::PushItemWidth(-1.0f); + ImGui::InputText("##CurrentNameValue", &m_current_property_value_buffer); + if (ImGui::Button("Apply")) + { + FOutputDevice placeholder_device{}; + StringType new_name = to_wstring(m_current_property_value_buffer); + FName new_key = FName(new_name, FNAME_Add); + unsigned char** value = rowmap.Find(FName(row_name)); + + // NOTE: Explicitly giving specifying template params for TPair because Clang can't handle TPair being a templated using statement. + udatatatble->AddRow(new_key, (FTableRowBase&)value); + + TArray OutRowNames{}; + udatatatble->GetAllRowNames(OutRowNames); + if (OutRowNames[index].ToString() != new_name) + { + m_modal_edit_property_value_error_unable_to_edit = true; + ImGui::OpenPopup("UnableToAddNewRowError"); + } + else + { + ImGui::CloseCurrentPopup(); + } + m_current_property_value_buffer.clear(); + } + + if (ImGui::BeginPopupModal("UnableToAddNewRowError", + &m_modal_edit_property_value_error_unable_to_edit, + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Was unable to insert new row."); + ImGui::EndPopup(); + } + + ImGui::EndPopup(); + } + + if (m_modal_edit_property_value_opened_this_frame) + { + m_modal_edit_property_value_opened_this_frame = false; + } + } + ImGui::EndTable(); // DataTable Table + } + auto LiveView::render_bottom_panel() -> void { const auto currently_selected_object = get_selected_object(); @@ -2311,6 +2569,11 @@ namespace RC::GUI { render_enum(); } + else if (currently_selected_object.second->IsA()) + { + render_properties(); + render_datatable(); + } else { render_properties();