From b2fc80f970f810152e4dd705e5d02516776dd3b9 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sat, 3 Aug 2024 11:32:17 +0200 Subject: [PATCH] impr: Fix various issues with runtime-generated language strings --- .../include/hex/api/content_registry.hpp | 2 +- .../include/hex/api/localization_manager.hpp | 55 +++++++++++-------- lib/libimhex/include/hex/api/task_manager.hpp | 16 +++--- lib/libimhex/include/hex/ui/widgets.hpp | 2 +- lib/libimhex/source/api/content_registry.cpp | 16 +++--- lib/libimhex/source/api/imhex_api.cpp | 2 +- .../source/api/localization_manager.cpp | 38 ++++++++++--- lib/libimhex/source/api/task_manager.cpp | 34 ++++++------ .../source/content/background_services.cpp | 4 +- .../source/content/settings_entries.cpp | 2 +- plugins/builtin/source/content/ui_items.cpp | 6 +- .../content/views/view_command_palette.cpp | 4 +- 12 files changed, 108 insertions(+), 73 deletions(-) diff --git a/lib/libimhex/include/hex/api/content_registry.hpp b/lib/libimhex/include/hex/api/content_registry.hpp index c6424336d..96d90fcdd 100644 --- a/lib/libimhex/include/hex/api/content_registry.hpp +++ b/lib/libimhex/include/hex/api/content_registry.hpp @@ -1273,7 +1273,7 @@ namespace hex { void stopServices(); } - void registerService(Lang name, const impl::Callback &callback); + void registerService(const UnlocalizedString &unlocalizedString, const impl::Callback &callback); } /* Network Communication Interface Registry. Allows adding new communication interface endpoints */ diff --git a/lib/libimhex/include/hex/api/localization_manager.hpp b/lib/libimhex/include/hex/api/localization_manager.hpp index 26cceba1c..eea1c9c2a 100644 --- a/lib/libimhex/include/hex/api/localization_manager.hpp +++ b/lib/libimhex/include/hex/api/localization_manager.hpp @@ -42,11 +42,13 @@ namespace hex { struct UnlocalizedString; + class LangConst; + class Lang { public: - Lang() = default; explicit Lang(const char *unlocalizedString); explicit Lang(const std::string &unlocalizedString); + explicit(false) Lang(const LangConst &localizedString); explicit Lang(const UnlocalizedString &unlocalizedString); explicit Lang(std::string_view unlocalizedString); @@ -56,9 +58,22 @@ namespace hex { const char* get() const; - constexpr static size_t hash(std::string_view string){ + private: + std::size_t m_entryHash; + std::string m_unlocalizedString; + }; + + class LangConst { + public: + [[nodiscard]] operator std::string() const; + [[nodiscard]] operator std::string_view() const; + [[nodiscard]] operator const char *() const; + + const char* get() const; + + constexpr static size_t hash(std::string_view string) { constexpr u64 p = 131; - constexpr u64 m = std::numeric_limits::max() - 4; // Largest 32 bit prime + constexpr u64 m = std::numeric_limits::max() - 4; u64 total = 0; u64 currentMultiplier = 1; @@ -71,36 +86,24 @@ namespace hex { } private: - constexpr explicit Lang(std::size_t hash, const char *unlocalizedString) : m_entryHash(hash), m_unlocalizedString(unlocalizedString) {} + constexpr explicit LangConst(std::size_t hash, const char *unlocalizedString) : m_entryHash(hash), m_unlocalizedString(unlocalizedString) {} template - friend consteval Lang operator""_lang(); + friend consteval LangConst operator""_lang(); + friend class Lang; private: std::size_t m_entryHash; const char *m_unlocalizedString = nullptr; }; - [[nodiscard]] std::string operator+(const std::string &&left, const Lang &&right); - [[nodiscard]] std::string operator+(const Lang &&left, const std::string &&right); - [[nodiscard]] std::string operator+(const std::string_view &&left, const Lang &&right); - [[nodiscard]] std::string operator+(const Lang &&left, const std::string_view &&right); - [[nodiscard]] std::string operator+(const char *left, const Lang &&right); - [[nodiscard]] std::string operator+(const Lang &&left, const char *right); - [[nodiscard]] std::string operator+(const Lang &&left, const Lang &&right); - - template - [[nodiscard]] consteval Lang operator""_lang() { - return Lang(Lang::hash(String.value.data()), String.value.data()); - } - - struct UnlocalizedString { public: UnlocalizedString() = default; - UnlocalizedString(auto && arg) : m_unlocalizedString(std::forward(arg)) { - static_assert(!std::same_as, Lang>, "Expected a unlocalized name, got a localized one!"); + template + UnlocalizedString(T &&arg) : m_unlocalizedString(std::forward(arg)) { + static_assert(!std::same_as, Lang>, "Expected a unlocalized name, got a localized one!"); } [[nodiscard]] operator std::string() const { @@ -132,9 +135,17 @@ namespace hex { std::string m_unlocalizedString; }; - // {fmt} formatter for hex::Lang + template + [[nodiscard]] consteval LangConst operator""_lang() { + return LangConst(LangConst::hash(String.value.data()), String.value.data()); + } + + // {fmt} formatter for hex::Lang and hex::LangConst inline auto format_as(const hex::Lang &entry) { return entry.get(); } + inline auto format_as(const hex::LangConst &entry) { + return entry.get(); + } } diff --git a/lib/libimhex/include/hex/api/task_manager.hpp b/lib/libimhex/include/hex/api/task_manager.hpp index 040d91d7c..562dab0a6 100644 --- a/lib/libimhex/include/hex/api/task_manager.hpp +++ b/lib/libimhex/include/hex/api/task_manager.hpp @@ -22,7 +22,7 @@ namespace hex { class Task { public: Task() = default; - Task(Lang name, u64 maxValue, bool background, std::function function); + Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function); Task(const Task&) = delete; Task(Task &&other) noexcept; @@ -65,7 +65,7 @@ namespace hex { void clearException(); [[nodiscard]] std::string getExceptionMessage() const; - [[nodiscard]] const Lang &getName(); + [[nodiscard]] const UnlocalizedString &getUnlocalizedName(); [[nodiscard]] u64 getValue() const; [[nodiscard]] u64 getMaxValue() const; @@ -77,7 +77,7 @@ namespace hex { private: mutable std::mutex m_mutex; - Lang m_name; + UnlocalizedString m_unlocalizedName; std::atomic m_currValue = 0, m_maxValue = 0; std::function m_interruptCallback; std::function m_function; @@ -130,20 +130,20 @@ namespace hex { /** * @brief Creates a new asynchronous task that gets displayed in the Task Manager in the footer - * @param name Name of the task + * @param unlocalizedName Name of the task * @param maxValue Maximum value of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ - static TaskHolder createTask(Lang name, u64 maxValue, std::function function); + static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function); /** * @brief Creates a new asynchronous task that does not get displayed in the Task Manager - * @param name Name of the task + * @param unlocalizedName Name of the task * @param function Function to be executed * @return A TaskHolder holding a weak reference to the task */ - static TaskHolder createBackgroundTask(Lang name, std::function function); + static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function); /** * @brief Creates a new synchronous task that will execute the given function at the start of the next frame @@ -190,7 +190,7 @@ namespace hex { static void runDeferredCalls(); private: - static TaskHolder createTask(Lang name, u64 maxValue, bool background, std::function function); + static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function); }; } \ No newline at end of file diff --git a/lib/libimhex/include/hex/ui/widgets.hpp b/lib/libimhex/include/hex/ui/widgets.hpp index 8e4bcf9b2..6ef8f967e 100644 --- a/lib/libimhex/include/hex/ui/widgets.hpp +++ b/lib/libimhex/include/hex/ui/widgets.hpp @@ -35,7 +35,7 @@ namespace hex::ui { m_filteredEntries.clear(); m_filteredEntries.reserve(entries.size()); - m_updateTask = TaskManager::createBackgroundTask(Lang("Searching"), [this, &entries, searchBuffer = m_searchBuffer](Task&) { + m_updateTask = TaskManager::createBackgroundTask("Searching", [this, &entries, searchBuffer = m_searchBuffer](Task&) { for (auto &entry : entries) { if (searchBuffer.empty() || m_comparator(searchBuffer, entry)) m_filteredEntries.push_back(&entry); diff --git a/lib/libimhex/source/api/content_registry.cpp b/lib/libimhex/source/api/content_registry.cpp index 8b053e475..caffba3a2 100644 --- a/lib/libimhex/source/api/content_registry.cpp +++ b/lib/libimhex/source/api/content_registry.cpp @@ -1199,7 +1199,7 @@ namespace hex { class Service { public: - Service(std::string name, std::jthread thread) : m_name(std::move(name)), m_thread(std::move(thread)) { } + Service(const UnlocalizedString &unlocalizedName, std::jthread thread) : m_unlocalizedName(std::move(unlocalizedName)), m_thread(std::move(thread)) { } Service(const Service&) = delete; Service(Service &&) = default; ~Service() { @@ -1211,8 +1211,8 @@ namespace hex { Service& operator=(const Service&) = delete; Service& operator=(Service &&) = default; - [[nodiscard]] const std::string& getName() const { - return m_name; + [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { + return m_unlocalizedName; } [[nodiscard]] const std::jthread& getThread() const { @@ -1220,7 +1220,7 @@ namespace hex { } private: - std::string m_name; + UnlocalizedString m_unlocalizedName; std::jthread m_thread; }; @@ -1235,13 +1235,13 @@ namespace hex { } - void registerService(Lang name, const impl::Callback &callback) { - log::debug("Registered new background service: {}", name.get()); + void registerService(const UnlocalizedString &unlocalizedName, const impl::Callback &callback) { + log::debug("Registered new background service: {}", unlocalizedName.get()); impl::s_services->emplace_back( - name, + unlocalizedName, std::jthread([=](const std::stop_token &stopToken){ - TaskManager::setCurrentThreadName(name); + TaskManager::setCurrentThreadName(Lang(unlocalizedName)); while (!stopToken.stop_requested()) { callback(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); diff --git a/lib/libimhex/source/api/imhex_api.cpp b/lib/libimhex/source/api/imhex_api.cpp index 89d018429..4d48152c2 100644 --- a/lib/libimhex/source/api/imhex_api.cpp +++ b/lib/libimhex/source/api/imhex_api.cpp @@ -450,7 +450,7 @@ namespace hex { // Do the destruction of the provider in the background once all tasks have finished TaskManager::runWhenTasksFinished([providerToRemove] { EventProviderDeleted::post(providerToRemove); - TaskManager::createBackgroundTask(Lang("Closing Provider"), [providerToRemove](Task &) { + TaskManager::createBackgroundTask("Closing Provider", [providerToRemove](Task &) { eraseMutex.lock(); auto provider = std::move((*s_providersToRemove)[providerToRemove]); s_providersToRemove->erase(providerToRemove); diff --git a/lib/libimhex/source/api/localization_manager.cpp b/lib/libimhex/source/api/localization_manager.cpp index 3d2a41aa6..c6255e347 100644 --- a/lib/libimhex/source/api/localization_manager.cpp +++ b/lib/libimhex/source/api/localization_manager.cpp @@ -51,7 +51,7 @@ namespace hex { if (value.empty()) continue; - s_currStrings->emplace(Lang::hash(key), value); + s_currStrings->emplace(LangConst::hash(key), value); } } } @@ -109,10 +109,11 @@ namespace hex { } - Lang::Lang(const char *unlocalizedString) : m_entryHash(hash(unlocalizedString)) { } - Lang::Lang(const std::string &unlocalizedString) : m_entryHash(hash(unlocalizedString)) { } - Lang::Lang(const UnlocalizedString &unlocalizedString) : m_entryHash(hash(unlocalizedString.get())) { } - Lang::Lang(std::string_view unlocalizedString) : m_entryHash(hash(unlocalizedString)) { } + Lang::Lang(const char *unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString)), m_unlocalizedString(unlocalizedString) { } + Lang::Lang(const std::string &unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString)), m_unlocalizedString(unlocalizedString) { } + Lang::Lang(const LangConst &localizedString) : m_entryHash(localizedString.m_entryHash), m_unlocalizedString(localizedString.m_unlocalizedString) { } + Lang::Lang(const UnlocalizedString &unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString.get())), m_unlocalizedString(unlocalizedString.get()) { } + Lang::Lang(std::string_view unlocalizedString) : m_entryHash(LangConst::hash(unlocalizedString)), m_unlocalizedString(unlocalizedString) { } Lang::operator std::string() const { return get(); @@ -129,9 +130,32 @@ namespace hex { const char *Lang::get() const { const auto &lang = *LocalizationManager::s_currStrings; - auto it = lang.find(m_entryHash); + const auto it = lang.find(m_entryHash); if (it == lang.end()) { - return m_unlocalizedString == nullptr ? "[ !!! INVALID LANGUAGE STRING !!! ]" : m_unlocalizedString; + return m_unlocalizedString.c_str(); + } else { + return it->second.c_str(); + } + } + + LangConst::operator std::string() const { + return get(); + } + + LangConst::operator std::string_view() const { + return get(); + } + + LangConst::operator const char *() const { + return get(); + } + + const char *LangConst::get() const { + const auto &lang = *LocalizationManager::s_currStrings; + + const auto it = lang.find(m_entryHash); + if (it == lang.end()) { + return m_unlocalizedString; } else { return it->second.c_str(); } diff --git a/lib/libimhex/source/api/task_manager.cpp b/lib/libimhex/source/api/task_manager.cpp index 655ccf2d4..5c494013a 100644 --- a/lib/libimhex/source/api/task_manager.cpp +++ b/lib/libimhex/source/api/task_manager.cpp @@ -63,8 +63,8 @@ namespace hex { } - Task::Task(Lang name, u64 maxValue, bool background, std::function function) - : m_name(std::move(name)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background) { } + Task::Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function) + : m_unlocalizedName(std::move(unlocalizedName)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background) { } Task::Task(hex::Task &&other) noexcept { { @@ -72,7 +72,7 @@ namespace hex { std::scoped_lock otherLock(other.m_mutex); m_function = std::move(other.m_function); - m_name = std::move(other.m_name); + m_unlocalizedName = std::move(other.m_unlocalizedName); } m_maxValue = u64(other.m_maxValue); @@ -159,8 +159,8 @@ namespace hex { return m_exceptionMessage; } - const Lang &Task::getName() { - return m_name; + const UnlocalizedString &Task::getUnlocalizedName() { + return m_unlocalizedName; } u64 Task::getValue() const { @@ -275,22 +275,22 @@ namespace hex { try { // Set the thread name to the name of the task - TaskManager::setCurrentThreadName(task->m_name); + TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName)); // Execute the task task->m_function(*task); - log::debug("Task '{}' finished", task->m_name.get()); + log::debug("Task '{}' finished", task->m_unlocalizedName.get()); } catch (const Task::TaskInterruptor &) { // Handle the task being interrupted by user request task->interruption(); } catch (const std::exception &e) { - log::error("Exception in task '{}': {}", task->m_name.get(), e.what()); + log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what()); // Handle the task throwing an uncaught exception task->exception(e.what()); } catch (...) { - log::error("Exception in task '{}'", task->m_name.get()); + log::error("Exception in task '{}'", task->m_unlocalizedName.get()); // Handle the task throwing an uncaught exception of unknown type task->exception("Unknown Exception"); @@ -327,11 +327,11 @@ namespace hex { s_tasksFinishedCallbacks.clear(); } - TaskHolder TaskManager::createTask(Lang name, u64 maxValue, bool background, std::function function) { + TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function function) { std::scoped_lock lock(s_queueMutex); // Construct new task - auto task = std::make_shared(std::move(name), maxValue, background, std::move(function)); + auto task = std::make_shared(std::move(unlocalizedName), maxValue, background, std::move(function)); s_tasks.emplace_back(task); @@ -344,14 +344,14 @@ namespace hex { } - TaskHolder TaskManager::createTask(Lang name, u64 maxValue, std::function function) { - log::debug("Creating task {}", name); - return createTask(std::move(name), maxValue, false, std::move(function)); + TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function function) { + log::debug("Creating task {}", unlocalizedName.get()); + return createTask(std::move(unlocalizedName), maxValue, false, std::move(function)); } - TaskHolder TaskManager::createBackgroundTask(Lang name, std::function function) { - log::debug("Creating background task {}", name); - return createTask(std::move(name), 0, true, std::move(function)); + TaskHolder TaskManager::createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function function) { + log::debug("Creating background task {}", unlocalizedName.get()); + return createTask(std::move(unlocalizedName), 0, true, std::move(function)); } void TaskManager::collectGarbage() { diff --git a/plugins/builtin/source/content/background_services.cpp b/plugins/builtin/source/content/background_services.cpp index cb43cc49a..3501c726a 100644 --- a/plugins/builtin/source/content/background_services.cpp +++ b/plugins/builtin/source/content/background_services.cpp @@ -111,8 +111,8 @@ namespace hex::plugin::builtin { s_autoBackupTime = value.get(0) * 30; }); - ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.network_interface"_lang, handleNetworkInterfaceService); - ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.auto_backup"_lang, handleAutoBackup); + ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.network_interface", handleNetworkInterfaceService); + ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.auto_backup", handleAutoBackup); EventProviderDirtied::subscribe([](prv::Provider *) { s_dataDirty = true; diff --git a/plugins/builtin/source/content/settings_entries.cpp b/plugins/builtin/source/content/settings_entries.cpp index 187005d70..e6b1cdca7 100644 --- a/plugins/builtin/source/content/settings_entries.cpp +++ b/plugins/builtin/source/content/settings_entries.cpp @@ -174,7 +174,7 @@ namespace hex::plugin::builtin { bool draw(const std::string &name) override { auto format = [this] -> std::string { if (m_value == 0) - return "hex.builtin.setting.interface.scaling.native"_lang + hex::format(" (x{:.1f})", ImHexApi::System::getNativeScale()); + return hex::format("{} (x{:.1f})", "hex.builtin.setting.interface.scaling.native"_lang, ImHexApi::System::getNativeScale()); else return "x%.1f"; }(); diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index aa147d55c..e29b58abc 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -42,7 +42,7 @@ namespace hex::plugin::builtin { // Task exception toast for (const auto &task : TaskManager::getRunningTasks()) { if (task->hadException()) { - ui::ToastError::open(hex::format("hex.builtin.popup.error.task_exception"_lang, task->getName(), task->getExceptionMessage())); + ui::ToastError::open(hex::format("hex.builtin.popup.error.task_exception"_lang, Lang(task->getUnlocalizedName()), task->getExceptionMessage())); task->clearException(); break; } @@ -268,7 +268,7 @@ namespace hex::plugin::builtin { else progressString = hex::format("[ {}/{} ({:.1f}%) ] ", frontTask->getValue(), frontTask->getMaxValue(), std::min(progress, 1.0F) * 100.0F); - ImGuiExt::InfoTooltip(hex::format("{}{}", progressString, frontTask->getName()).c_str()); + ImGuiExt::InfoTooltip(hex::format("{}{}", progressString, Lang(frontTask->getUnlocalizedName())).c_str()); if (ImGui::BeginPopupContextItem("RestTasks", ImGuiPopupFlags_MouseButtonLeft)) { for (const auto &task : tasks) { @@ -276,7 +276,7 @@ namespace hex::plugin::builtin { continue; ImGui::PushID(&task); - ImGuiExt::TextFormatted("{}", task->getName()); + ImGuiExt::TextFormatted("{}", Lang(task->getUnlocalizedName())); ImGui::SameLine(); ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); ImGui::SameLine(); diff --git a/plugins/builtin/source/content/views/view_command_palette.cpp b/plugins/builtin/source/content/views/view_command_palette.cpp index 19562d49c..6c8d298af 100644 --- a/plugins/builtin/source/content/views/view_command_palette.cpp +++ b/plugins/builtin/source/content/views/view_command_palette.cpp @@ -166,7 +166,7 @@ namespace hex::plugin::builtin { if (auto [match, value] = MatchCommand(input, command); match != MatchType::NoMatch) { if (match != MatchType::PerfectMatch) { - results.push_back({ command + " (" + Lang(unlocalizedDescription) + ")", "", AutoComplete }); + results.push_back({ hex::format("{} ({})", command, Lang(unlocalizedDescription)), "", AutoComplete }); } else { auto matchedCommand = wolv::util::trim(input.substr(command.length())); results.push_back({ displayCallback(matchedCommand), matchedCommand, executeCallback }); @@ -178,7 +178,7 @@ namespace hex::plugin::builtin { if (auto [match, value] = MatchCommand(input, command + " "); match != MatchType::NoMatch) { if (match != MatchType::PerfectMatch) { - results.push_back({ command + " (" + Lang(unlocalizedDescription) + ")", "", AutoComplete }); + results.push_back({ hex::format("{} ({})", command, Lang(unlocalizedDescription)), "", AutoComplete }); } else { auto matchedCommand = wolv::util::trim(input.substr(command.length() + 1)); results.push_back({ displayCallback(matchedCommand), matchedCommand, executeCallback });