1
0
mirror of synced 2025-01-31 03:53:44 +01:00

impr: Fix various issues with runtime-generated language strings

This commit is contained in:
WerWolv 2024-08-03 11:32:17 +02:00
parent efee128c1c
commit b2fc80f970
12 changed files with 108 additions and 73 deletions

View File

@ -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 */

View File

@ -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<std::uint32_t>::max() - 4; // Largest 32 bit prime
constexpr u64 m = std::numeric_limits<std::uint32_t>::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<wolv::type::StaticString>
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<wolv::type::StaticString String>
[[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<decltype(arg)>(arg)) {
static_assert(!std::same_as<std::remove_cvref_t<decltype(arg)>, Lang>, "Expected a unlocalized name, got a localized one!");
template<typename T>
UnlocalizedString(T &&arg) : m_unlocalizedString(std::forward<T>(arg)) {
static_assert(!std::same_as<std::remove_cvref_t<T>, 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<wolv::type::StaticString String>
[[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();
}
}

View File

@ -22,7 +22,7 @@ namespace hex {
class Task {
public:
Task() = default;
Task(Lang name, u64 maxValue, bool background, std::function<void(Task &)> function);
Task(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> 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<u64> m_currValue = 0, m_maxValue = 0;
std::function<void()> m_interruptCallback;
std::function<void(Task &)> 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<void(Task &)> function);
static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, std::function<void(Task &)> 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<void(Task &)> function);
static TaskHolder createBackgroundTask(const UnlocalizedString &unlocalizedName, std::function<void(Task &)> 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<void(Task &)> function);
static TaskHolder createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
};
}

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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();
}

View File

@ -63,8 +63,8 @@ namespace hex {
}
Task::Task(Lang name, u64 maxValue, bool background, std::function<void(Task &)> 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<void(Task &)> 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<void(Task&)> function) {
TaskHolder TaskManager::createTask(const UnlocalizedString &unlocalizedName, u64 maxValue, bool background, std::function<void(Task&)> function) {
std::scoped_lock lock(s_queueMutex);
// Construct new task
auto task = std::make_shared<Task>(std::move(name), maxValue, background, std::move(function));
auto task = std::make_shared<Task>(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<void(Task &)> 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<void(Task &)> function) {
log::debug("Creating task {}", unlocalizedName.get());
return createTask(std::move(unlocalizedName), maxValue, false, std::move(function));
}
TaskHolder TaskManager::createBackgroundTask(Lang name, std::function<void(Task &)> 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<void(Task &)> function) {
log::debug("Creating background task {}", unlocalizedName.get());
return createTask(std::move(unlocalizedName), 0, true, std::move(function));
}
void TaskManager::collectGarbage() {

View File

@ -111,8 +111,8 @@ namespace hex::plugin::builtin {
s_autoBackupTime = value.get<int>(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;

View File

@ -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";
}();

View File

@ -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();

View File

@ -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 });