From 56e4210b7f8a4358e4ec3f89339ae2b3e62bdffc Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Wed, 18 Sep 2024 10:16:39 -0500 Subject: [PATCH 1/9] Model out basic interface --- include/resource/resource.hpp | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 include/resource/resource.hpp diff --git a/include/resource/resource.hpp b/include/resource/resource.hpp new file mode 100644 index 00000000..743abea6 --- /dev/null +++ b/include/resource/resource.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +#include "core/error.hpp" +#include "fsystem.hpp" +#include "serial.hpp" +#include "unique.hpp" + +namespace Toolbox { + + class ResourcePath : public IUnique { + public: + ResourcePath() = default; + ResourcePath(const ResourcePath &) = default; + ResourcePath(ResourcePath &&) = default; + + [[nodiscard]] fs_path getPath() const { return m_path; } + void setPath(const fs_path &path) { m_path = path; } + void setPath(fs_path &&path) { m_path = std::move(path); } + + [[nodiscard]] UUID64 getUUID() const { return m_uuid; } + + private: + UUID64 m_uuid; + fs_path m_path; + }; + + class ResourceManager : public IUnique { + public: + ResourceManager() = default; + ResourceManager(const ResourceManager &) = default; + ResourceManager(ResourceManager &&) = default; + + ~ResourceManager(); + + [[nodiscard]] UUID64 getUUID() const { return m_uuid; } + + void includePath(const fs_path &path); + void includePath(fs_path &&path); + + void removePath(const fs_path &path); + void removePath(fs_path &&path); + + [[nodiscard]] bool hasResourcePath(const fs_path &path) const; + [[nodiscard]] bool hasResourcePath(fs_path &&path) const; + [[nodiscard]] bool hasResourcePath(const UUID64 &path_uuid) const; + + [[nodiscard]] bool hasPath(const fs_path &path, + const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] bool hasPath(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + + [[nodiscard]] Result, FSError> + getImageHandle(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] Result, FSError> + getImageHandle(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + + [[nodiscard]] Result + getSerialData(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] Result + getSerialData(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + + [[nodiscard]] Result, FSError> + getRawData(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] Result, FSError> + getRawData(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + + protected: + std::optional getResourcePath(const UUID64 &path_uuid) const; + UUID64 getResourcePathUUID(const fs_path &path) const; + UUID64 getResourcePathUUID(fs_path &&path) const; + + private: + UUID64 m_uuid; + std::vector m_resource_paths; + + mutable std::unordered_map> m_image_handle_cache; + }; + +} // namespace Toolbox \ No newline at end of file From c9f6b475a9fc6a76cac3b0430db47e41af39db3b Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Wed, 25 Sep 2024 11:46:51 -0500 Subject: [PATCH 2/9] Untested implementation --- CMakeLists.txt | 2 + include/resource/resource.hpp | 38 ++-- include/serial.hpp | 2 + lib/imgui | 2 +- src/resource/resource.cpp | 361 ++++++++++++++++++++++++++++++++++ 5 files changed, 391 insertions(+), 14 deletions(-) create mode 100644 src/resource/resource.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 44179d9f..21885e28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,8 @@ file(GLOB TOOLBOX_SRC "include/rail/*.hpp" "src/rarc/*.cpp" "include/rarc/*.hpp" + "src/resource/*.cpp" + "include/resource/*.hpp" "src/scene/*.cpp" "include/scene/*.hpp" "src/window/*.cpp" diff --git a/include/resource/resource.hpp b/include/resource/resource.hpp index 743abea6..e0cdd28f 100644 --- a/include/resource/resource.hpp +++ b/include/resource/resource.hpp @@ -6,6 +6,7 @@ #include "core/error.hpp" #include "fsystem.hpp" +#include "image/imagehandle.hpp" #include "serial.hpp" #include "unique.hpp" @@ -17,6 +18,8 @@ namespace Toolbox { ResourcePath(const ResourcePath &) = default; ResourcePath(ResourcePath &&) = default; + ResourcePath(const fs_path &path, const UUID64 &uuid = 0); + [[nodiscard]] fs_path getPath() const { return m_path; } void setPath(const fs_path &path) { m_path = path; } void setPath(fs_path &&path) { m_path = std::move(path); } @@ -28,6 +31,11 @@ namespace Toolbox { fs_path m_path; }; + struct ResourceData { + size_t m_data_size; + void *m_data_ptr; + }; + class ResourceManager : public IUnique { public: ResourceManager() = default; @@ -38,45 +46,49 @@ namespace Toolbox { [[nodiscard]] UUID64 getUUID() const { return m_uuid; } - void includePath(const fs_path &path); - void includePath(fs_path &&path); + static UUID64 getResourcePathUUID(const fs_path &path); + static UUID64 getResourcePathUUID(fs_path &&path); + + void includeResourcePath(const fs_path &path, bool preload_files); + void includeResourcePath(fs_path &&path, bool preload_files); - void removePath(const fs_path &path); - void removePath(fs_path &&path); + void removeResourcePath(const fs_path &path); + void removeResourcePath(fs_path &&path); + void removeResourcePath(const UUID64 &path_uuid); [[nodiscard]] bool hasResourcePath(const fs_path &path) const; [[nodiscard]] bool hasResourcePath(fs_path &&path) const; [[nodiscard]] bool hasResourcePath(const UUID64 &path_uuid) const; - [[nodiscard]] bool hasPath(const fs_path &path, - const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] bool hasPath(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] bool hasDataPath(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] bool hasDataPath(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; [[nodiscard]] Result, FSError> getImageHandle(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; [[nodiscard]] Result, FSError> getImageHandle(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] Result + [[nodiscard]] Result getSerialData(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] Result + [[nodiscard]] Result getSerialData(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] Result, FSError> + [[nodiscard]] Result, FSError> getRawData(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] Result, FSError> + [[nodiscard]] Result, FSError> getRawData(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; protected: std::optional getResourcePath(const UUID64 &path_uuid) const; - UUID64 getResourcePathUUID(const fs_path &path) const; - UUID64 getResourcePathUUID(fs_path &&path) const; + std::optional findResourcePath(const fs_path &sub_path) const; + void preloadData(const fs_path &resource_path) const; private: UUID64 m_uuid; std::vector m_resource_paths; mutable std::unordered_map> m_image_handle_cache; + mutable std::unordered_map m_data_preload_cache; }; } // namespace Toolbox \ No newline at end of file diff --git a/include/serial.hpp b/include/serial.hpp index 24b35abb..b780c650 100644 --- a/include/serial.hpp +++ b/include/serial.hpp @@ -191,6 +191,8 @@ namespace Toolbox { Deserializer(std::streambuf *in) : m_in(in) {} Deserializer(std::streambuf *in, std::string_view file_path) : m_in(in), m_file_path(file_path) {} + Deserializer(std::fstream &&in, std::string_view file_path) + : m_in(in), m_file_path(file_path) {} Deserializer(const Deserializer &) = default; Deserializer(Deserializer &&) = default; diff --git a/lib/imgui b/lib/imgui index e391fe2e..f63c95a0 160000 --- a/lib/imgui +++ b/lib/imgui @@ -1 +1 @@ -Subproject commit e391fe2e66eb1c96b1624ae8444dc64c23146ef4 +Subproject commit f63c95a076a401721ceaf21f30d4f12e8c40cb2c diff --git a/src/resource/resource.cpp b/src/resource/resource.cpp new file mode 100644 index 00000000..b75e0bc9 --- /dev/null +++ b/src/resource/resource.cpp @@ -0,0 +1,361 @@ +#include + +#include "resource/resource.hpp" + +namespace Toolbox { + + ResourceManager::~ResourceManager() {} + + void ResourceManager::includeResourcePath(const fs_path &path, bool preload_files) { + if (hasResourcePath(path)) { + return; + } + + m_resource_paths.emplace_back(path); + + if (!preload_files) { + return; + } + + preloadData(path); + } + + void ResourceManager::includeResourcePath(fs_path &&path, bool preload_files) { + if (hasResourcePath(path)) { + return; + } + + m_resource_paths.emplace_back(path); + + if (!preload_files) { + return; + } + + preloadData(std::move(path)); + } + + void ResourceManager::removeResourcePath(const fs_path &path) { + UUID64 path_uuid = getResourcePathUUID(path); + removeResourcePath(path_uuid); + } + + void ResourceManager::removeResourcePath(fs_path &&path) { + UUID64 path_uuid = getResourcePathUUID(std::move(path)); + removeResourcePath(path_uuid); + } + + void ResourceManager::removeResourcePath(const UUID64 &path_uuid) { + m_resource_paths.erase(std::remove_if(m_resource_paths.begin(), m_resource_paths.end(), + [&path_uuid](const ResourcePath &resource_path) { + return resource_path.getUUID() == path_uuid; + }), + m_resource_paths.end()); + } + + bool ResourceManager::hasResourcePath(const fs_path &path) const { + return std::any_of( + m_resource_paths.begin(), m_resource_paths.end(), + [&path](const ResourcePath &resource_path) { return resource_path.getPath() == path; }); + } + + bool ResourceManager::hasResourcePath(fs_path &&path) const { + return std::any_of( + m_resource_paths.begin(), m_resource_paths.end(), + [path](const ResourcePath &resource_path) { return resource_path.getPath() == path; }); + } + + bool ResourceManager::hasResourcePath(const UUID64 &path_uuid) const { + return std::any_of( + m_resource_paths.begin(), m_resource_paths.end(), + [&path_uuid](const ResourcePath &path) { return path.getUUID() == path_uuid; }); + } + + bool ResourceManager::hasDataPath(const fs_path &path, const UUID64 &resource_path_uuid) const { + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { + return true; + } + + fs_path resource_path; + + if (resource_path_uuid) { + std::optional result = getResourcePath(resource_path_uuid); + if (!result.has_value()) { + return false; + } + resource_path = std::move(result.value()); + } else { + std::optional result = findResourcePath(path); + if (!result.has_value()) { + return false; + } + resource_path = std::move(result.value()); + } + + fs_path abs_path = resource_path / path; + if (m_data_preload_cache.find(abs_path) != m_data_preload_cache.end()) { + return true; + } + + bool result = false; + Filesystem::is_regular_file(abs_path).and_then([&result](bool is_regular_file) { + result = is_regular_file; + return Result(); + }); + + return result; + } + + bool ResourceManager::hasDataPath(fs_path &&path, const UUID64 &resource_path_uuid) const { + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { + return true; + } + + fs_path resource_path; + + if (resource_path_uuid) { + std::optional result = getResourcePath(resource_path_uuid); + if (!result.has_value()) { + return false; + } + resource_path = std::move(result.value()); + } else { + std::optional result = findResourcePath(path); + if (!result.has_value()) { + return false; + } + resource_path = std::move(result.value()); + } + + fs_path abs_path = resource_path / path; + if (m_data_preload_cache.find(abs_path) != m_data_preload_cache.end()) { + return true; + } + + bool result = false; + Filesystem::is_regular_file(abs_path).and_then([&result](bool is_regular_file) { + result = is_regular_file; + return Result(); + }); + + return result; + } + + Result, FSError> + ResourceManager::getImageHandle(const fs_path &path, const UUID64 &resource_path_uuid) const { + return Result, FSError>(); + } + + Result, FSError> + ResourceManager::getImageHandle(fs_path &&path, const UUID64 &resource_path_uuid) const { + return Result, FSError>(); + } + + Result + ResourceManager::getSerialData(const fs_path &path, const UUID64 &resource_path_uuid) const { + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error(std::error_code(), {"Resource path not found"}); + } + + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + fs_path abs_path = resource_path / path; + + std::ifstream in(abs_path, std::ios::in | std::ios::binary); + if (!in.is_open()) { + return make_fs_error(std::error_code(), {"Failed to open file"}); + } + + return in; + } + + Result + ResourceManager::getSerialData(fs_path &&path, const UUID64 &resource_path_uuid) const { + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error(std::error_code(), {"Resource path not found"}); + } + + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + fs_path abs_path = resource_path / path; + + std::ifstream in(abs_path, std::ios::in | std::ios::binary); + if (!in.is_open()) { + return make_fs_error(std::error_code(), {"Failed to open file"}); + } + + return in; + } + + Result, FSError> + ResourceManager::getRawData(const fs_path &path, const UUID64 &resource_path_uuid) const { + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { + const ResourceData &data = m_data_preload_cache[path]; + return std::span(static_cast(data.m_data_ptr), data.m_data_size); + } + + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), {"Resource path not found"}); + } + + fs_path resource_path; + + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + fs_path abs_path = resource_path / path; + + if (m_data_preload_cache.find(abs_path) != m_data_preload_cache.end()) { + const ResourceData &data = m_data_preload_cache[abs_path]; + return std::span(static_cast(data.m_data_ptr), data.m_data_size); + } + + u64 file_size = 0; + Filesystem::file_size(abs_path).and_then([&file_size](u64 size) { + file_size = size; + return Result(); + }); + + if (file_size == 0) { + return make_fs_error>(std::error_code(), {"File size is 0"}); + } + + void *buffer = std::malloc(file_size); + if (!buffer) { + return make_fs_error>(std::error_code(), {"Failed to allocate memory for file buffer"}); + } + + std::fstream in(abs_path, std::ios::in | std::ios::binary); + in.read(static_cast(buffer), file_size); + in.close(); + + m_data_preload_cache[abs_path] = {file_size, buffer}; + return std::span(static_cast(buffer), file_size); + } + + Result, FSError> + ResourceManager::getRawData(fs_path &&path, const UUID64 &resource_path_uuid) const { + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { + const ResourceData &data = m_data_preload_cache[path]; + return std::span(static_cast(data.m_data_ptr), data.m_data_size); + } + + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), {"Resource path not found"}); + } + + fs_path resource_path; + + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + fs_path abs_path = resource_path / path; + + if (m_data_preload_cache.find(abs_path) != m_data_preload_cache.end()) { + const ResourceData &data = m_data_preload_cache[abs_path]; + return std::span(static_cast(data.m_data_ptr), data.m_data_size); + } + + u64 file_size = 0; + Filesystem::file_size(abs_path).and_then([&file_size](u64 size) { + file_size = size; + return Result(); + }); + + if (file_size == 0) { + return make_fs_error>(std::error_code(), {"File size is 0"}); + } + + void *buffer = std::malloc(file_size); + if (!buffer) { + return make_fs_error>(std::error_code(), + {"Failed to allocate memory for file buffer"}); + } + + std::fstream in(abs_path, std::ios::in | std::ios::binary); + in.read(static_cast(buffer), file_size); + in.close(); + + m_data_preload_cache[abs_path] = {file_size, buffer}; + return std::span(static_cast(buffer), file_size); + } + + std::optional ResourceManager::getResourcePath(const UUID64 &path_uuid) const { + for (const ResourcePath &resource_path : m_resource_paths) { + if (resource_path.getUUID() == path_uuid) { + return resource_path.getPath(); + } + } + return std::nullopt; + } + + std::optional ResourceManager::findResourcePath(const fs_path &sub_path) const { + std::optional path = std::nullopt; + for (const ResourcePath &resource_path : m_resource_paths) { + fs_path p = resource_path.getPath() / sub_path; + Filesystem::is_directory(p).and_then([&path, &resource_path](bool is_directory) { + if (is_directory) { + path = resource_path.getPath(); + } + return Result(); + }); + + if (path.has_value()) { + break; + } + + Filesystem::is_regular_file(p).and_then([&path, &resource_path](bool is_regular_file) { + if (is_regular_file) { + path = resource_path.getPath(); + } + return Result(); + }); + + if (path.has_value()) { + break; + } + } + return path; + } + + UUID64 ResourceManager::getResourcePathUUID(const fs_path &path) { + return UUID64(std::hash{}(path)); + } + + UUID64 ResourceManager::getResourcePathUUID(fs_path &&path) { + return UUID64(std::hash{}(std::move(path))); + } + + void ResourceManager::preloadData(const fs_path &resource_path) const { + std::for_each( + std::execution::par_unseq, Filesystem::directory_iterator(resource_path), + Filesystem::directory_iterator(), [this](const fs_path &file) { + Filesystem::is_regular_file(file).and_then([this, &file](bool is_regular_file) { + if (!is_regular_file) { + return Result(); + } + + Filesystem::file_size(file).and_then([this, &file](u64 file_size) { + if (file_size == 0) { + return Result(); + } + + void *buffer = std::malloc(file_size); + if (!buffer) { + return make_fs_error( + std::error_code(), {"Failed to allocate memory for file buffer"}); + } + + std::fstream in(file, std::ios::in | std::ios::binary); + in.read(static_cast(buffer), file_size); + in.close(); + + m_data_preload_cache[file] = {file_size, buffer}; + + return Result(); + }); + + return Result(); + }); + }); + } + + ResourcePath::ResourcePath(const fs_path &path, const UUID64 &uuid) { + m_path = path; + m_uuid = uuid ? uuid : ResourceManager::getResourcePathUUID(path); + } + +} // namespace Toolbox \ No newline at end of file From b120469a411340d5fcd95586e91eff9dc1017449 Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Wed, 25 Sep 2024 11:57:48 -0500 Subject: [PATCH 3/9] Implement image handle no tests --- include/image/imagehandle.hpp | 6 +++ src/image/imagehandle.cpp | 39 +++++++++++++++++-- src/resource/resource.cpp | 73 +++++++++++++++++++++++++++++++++-- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/include/image/imagehandle.hpp b/include/image/imagehandle.hpp index d6290088..46750d02 100644 --- a/include/image/imagehandle.hpp +++ b/include/image/imagehandle.hpp @@ -3,6 +3,7 @@ #include "core/memory.hpp" #include "fsystem.hpp" #include +#include namespace Toolbox { @@ -21,6 +22,11 @@ namespace Toolbox { ImageHandle(const std::filesystem::path &res_path); + ImageHandle(std::span data); + ImageHandle(std::span data, int channels, int dx, int dy); + + ImageHandle(const Buffer &data); + // Data should be a valid RED, RGB, or RGBA image as loaded by stbi ImageHandle(const Buffer &data, int channels, int dx, int dy); diff --git a/src/image/imagehandle.cpp b/src/image/imagehandle.cpp index 1a3b1d4a..b135d8d5 100644 --- a/src/image/imagehandle.cpp +++ b/src/image/imagehandle.cpp @@ -21,8 +21,6 @@ namespace Toolbox { ImageHandle::ImageHandle(ImageHandle &&other) noexcept { moveGL(std::move(other)); } ImageHandle::ImageHandle(const std::filesystem::path &res_path) { - std::ifstream in(res_path, std::ios::in | std::ios::binary); - int width, height, channels; stbi_uc *data = stbi_load(res_path.string().c_str(), &width, &height, &channels, 0); @@ -34,6 +32,39 @@ namespace Toolbox { stbi_image_free(data); } + ImageHandle::ImageHandle(std::span data) { + int width, height, channels; + stbi_uc *stbi_buf = + stbi_load_from_memory(data.data(), data.size(), &width, &height, &channels, 0); + + Buffer data_buf; + data_buf.setBuf(stbi_buf, static_cast(width * height * channels)); + + loadGL(data_buf, channels, width, height); + + stbi_image_free(stbi_buf); + } + + ImageHandle::ImageHandle(std::span data, int channels, int dx, int dy) { + Buffer data_buf; + data_buf.setBuf(data.data(), data.size()); + + loadGL(data_buf, channels, dx, dy); + } + + ImageHandle::ImageHandle(const Buffer &data) { + int width, height, channels; + stbi_uc *stbi_buf = + stbi_load_from_memory(data.buf(), data.size(), &width, &height, &channels, 0); + + Buffer data_buf; + data_buf.setBuf(stbi_buf, static_cast(width * height * channels)); + + loadGL(data_buf, channels, width, height); + + stbi_image_free(stbi_buf); + } + ImageHandle::ImageHandle(const Buffer &data, int channels, int dx, int dy) { loadGL(data, channels, dx, dy); } @@ -108,7 +139,7 @@ namespace Toolbox { m_image_handle = (u64)handle; m_image_format = image.m_image_format; m_image_width = image.m_image_width; - m_image_height = image.m_image_height; + m_image_height = image.m_image_height; } else { m_image_handle = std::numeric_limits::max(); m_image_format = 0; @@ -138,4 +169,4 @@ namespace Toolbox { m_image_height = 0; } -} // namespace Toolbox::UI \ No newline at end of file +} // namespace Toolbox \ No newline at end of file diff --git a/src/resource/resource.cpp b/src/resource/resource.cpp index b75e0bc9..0e48b860 100644 --- a/src/resource/resource.cpp +++ b/src/resource/resource.cpp @@ -142,12 +142,78 @@ namespace Toolbox { Result, FSError> ResourceManager::getImageHandle(const fs_path &path, const UUID64 &resource_path_uuid) const { - return Result, FSError>(); + if (m_image_handle_cache.find(path) != m_image_handle_cache.end()) { + return Result, FSError>(m_image_handle_cache[path]); + } + + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { + const ResourceData &data = m_data_preload_cache[path]; + + std::span data_span(static_cast(data.m_data_ptr), data.m_data_size); + RefPtr handle = make_referable(data_span); + + if (!handle->isValid()) { + return make_fs_error>(std::error_code(), + {"Failed to load image"}); + } + + m_image_handle_cache[path] = handle; + return handle; + } + + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), + {"Resource path not found"}); + } + + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + fs_path abs_path = resource_path / path; + + RefPtr handle = make_referable(abs_path); + if (!handle->isValid()) { + return make_fs_error>(std::error_code(), {"Failed to load image"}); + } + + m_image_handle_cache[path] = handle; + return handle; } Result, FSError> ResourceManager::getImageHandle(fs_path &&path, const UUID64 &resource_path_uuid) const { - return Result, FSError>(); + if (m_image_handle_cache.find(path) != m_image_handle_cache.end()) { + return Result, FSError>(m_image_handle_cache[path]); + } + + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { + const ResourceData &data = m_data_preload_cache[path]; + + std::span data_span(static_cast(data.m_data_ptr), data.m_data_size); + RefPtr handle = make_referable(data_span); + + if (!handle->isValid()) { + return make_fs_error>(std::error_code(), + {"Failed to load image"}); + } + + m_image_handle_cache[path] = handle; + return handle; + } + + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), + {"Resource path not found"}); + } + + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + fs_path abs_path = resource_path / path; + + RefPtr handle = make_referable(abs_path); + if (!handle->isValid()) { + return make_fs_error>(std::error_code(), {"Failed to load image"}); + } + + m_image_handle_cache[path] = handle; + return handle; } Result @@ -217,7 +283,8 @@ namespace Toolbox { void *buffer = std::malloc(file_size); if (!buffer) { - return make_fs_error>(std::error_code(), {"Failed to allocate memory for file buffer"}); + return make_fs_error>(std::error_code(), + {"Failed to allocate memory for file buffer"}); } std::fstream in(abs_path, std::ios::in | std::ios::binary); From 5877d3635465c4148d84d34753376731c814e0c7 Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Wed, 11 Sep 2024 11:04:55 -0500 Subject: [PATCH 4/9] Update imgui_ext and fix bugs --- src/gui/application.cpp | 2 +- src/gui/imgui_ext.cpp | 645 ++++++++++++++++++++++------------------ 2 files changed, 363 insertions(+), 284 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 6e34fd37..7695a76e 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -308,7 +308,7 @@ namespace Toolbox { ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); - m_dockspace_id = ImGui::DockSpaceOverViewport(viewport); + m_dockspace_id = ImGui::DockSpaceOverViewport(0, viewport); m_dockspace_built = ImGui::DockBuilderGetNode(m_dockspace_id); if (!m_dockspace_built) { diff --git a/src/gui/imgui_ext.cpp b/src/gui/imgui_ext.cpp index 138b57f4..b74429c5 100644 --- a/src/gui/imgui_ext.cpp +++ b/src/gui/imgui_ext.cpp @@ -30,6 +30,7 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = { {sizeof(float), "float", "%.3f", "%f" }, // ImGuiDataType_Float (float are promoted to double in va_arg) {sizeof(double), "double", "%f", "%lf" }, // ImGuiDataType_Double + {sizeof(bool), "bool", "%d", "%d" }, // ImGuiDataType_Bool }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); @@ -353,7 +354,7 @@ bool ImGui::AlignedButton(const char *label, ImVec2 size, ImGuiButtonFlags flags return ret; } -bool ImGui::SwitchButton(const char* label, bool active, ImVec2 size, ImGuiButtonFlags flags) { +bool ImGui::SwitchButton(const char *label, bool active, ImVec2 size, ImGuiButtonFlags flags) { return SwitchButton(label, active, size, flags, ImDrawFlags_RoundCornersNone); } @@ -562,6 +563,22 @@ bool ImGui::TreeNodeEx(const char *label, ImGuiTreeNodeFlags flags, bool focused return TreeNodeBehavior(window->GetID(label), flags, label, NULL, focused); } +// Store ImGuiTreeNodeStackData for just submitted node. +// Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, +// easy to increase. +static void TreeNodeStoreStackData(ImGuiTreeNodeFlags flags) { + ImGuiContext &g = *GImGui; + ImGuiWindow *window = g.CurrentWindow; + + g.TreeNodeStack.resize(g.TreeNodeStack.Size + 1); + ImGuiTreeNodeStackData *tree_node_data = &g.TreeNodeStack.back(); + tree_node_data->ID = g.LastItemData.ID; + tree_node_data->TreeFlags = flags; + tree_node_data->InFlags = g.LastItemData.InFlags; + tree_node_data->NavRect = g.LastItemData.NavRect; + window->DC.TreeHasStackDataDepthMask |= (1 << window->DC.TreeDepth); +} + bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *label, const char *label_end, bool focused) { ImGuiWindow *window = GetCurrentWindow(); @@ -581,6 +598,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l label_end = FindRenderedTextEnd(label); const ImVec2 label_size = CalcTextSize(label, label_end, false); + const float text_offset_x = + g.FontSize + + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing + const float text_offset_y = + ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow + // We vertically grow up to current line height up the typical widget height. const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), @@ -592,73 +616,70 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; frame_bb.Min.y = window->DC.CursorPos.y; - frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x + : (flags & ImGuiTreeNodeFlags_SpanTextWidth) + ? window->DC.CursorPos.x + text_width + padding.x + : window->WorkRect.Max.x; frame_bb.Max.y = window->DC.CursorPos.y + frame_height; if (display_frame) { - // Framed header expand a little outside the default padding, to the edge of InnerClipRect - // (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x - // instead of WindowPadding.x*0.5f) - frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f); + const float outer_extend = + IM_TRUNC(window->WindowPadding.x * + 0.5f); // Framed header expand a little outside of current limits + frame_bb.Min.x -= outer_extend; + frame_bb.Max.x += outer_extend; } - const float text_offset_x = - g.FontSize + - (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing - const float text_offset_y = - ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 - : 0.0f); // Include collapsing ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); ItemSize(ImVec2(text_width, frame_height), padding.y); // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing ImRect interact_bb = frame_bb; - if (!display_frame && - (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | + if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | + ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) - interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f; - - // Modify ClipRect for the ItemAdd(), faster than doing a - // PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. - const float backup_clip_rect_min_x = window->ClipRect.Min.x; - const float backup_clip_rect_max_x = window->ClipRect.Max.x; - if (span_all_columns) { - window->ClipRect.Min.x = window->ParentWorkRect.Min.x; - window->ClipRect.Max.x = window->ParentWorkRect.Max.x; - } + interact_bb.Max.x = + frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); // Compute open and multi-select states before ItemAdd() as it clear NextItem data. - bool is_open = TreeNodeUpdateNextOpen(id, flags); - bool item_add = ItemAdd(interact_bb, id); - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - g.LastItemData.DisplayRect = frame_bb; + ImGuiID storage_id = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasStorageID) + ? g.NextItemData.StorageId + : id; + bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); + bool is_visible; if (span_all_columns) { - window->ClipRect.Min.x = backup_clip_rect_min_x; - window->ClipRect.Max.x = backup_clip_rect_max_x; + // Modify ClipRect for the ItemAdd(), faster than doing a + // PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + is_visible = ItemAdd(interact_bb, id); + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } else { + is_visible = ItemAdd(interact_bb, id); } + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; + g.LastItemData.DisplayRect = frame_bb; // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled: // Store data for the current depth to allow returning to this node from any child item. // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() // and TreePop(). It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by - // default or move it to ImGuiStyle. Currently only supports 32 level deep and we are fine with - // (1 << Depth) overflowing into a zero, easy to increase. - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && - !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && - NavMoveRequestButNoResultYet()) { - g.NavTreeNodeStack.resize(g.NavTreeNodeStack.Size + 1); - ImGuiNavTreeNodeData *nav_tree_node_data = &g.NavTreeNodeStack.back(); - nav_tree_node_data->ID = id; - nav_tree_node_data->InFlags = g.LastItemData.InFlags; - nav_tree_node_data->NavRect = g.LastItemData.NavRect; - window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); - } + // default or move it to ImGuiStyle. + bool store_tree_node_stack_data = false; + if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) { + if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive) + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && + NavMoveRequestButNoResultYet()) + store_tree_node_stack_data = true; + } const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; - if (!item_add) { + if (!is_visible) { + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, @@ -668,8 +689,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l return is_open; } - if (span_all_columns) + if (span_all_columns) { TablePushBackgroundChannel(); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; + g.LastItemData.ClipRect = window->ClipRect; + } ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || @@ -686,8 +710,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); - if (window != g.HoveredWindow || !is_mouse_x_over_arrow) - button_flags |= ImGuiButtonFlags_NoKeyModifiers; + + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes + // by default + flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 + ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick + : ImGuiTreeNodeFlags_OpenOnArrow; // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to @@ -711,27 +740,40 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; const bool was_selected = selected; + // Multi-selection support (header) + if (is_multi_select) { + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); + if (is_mouse_x_over_arrow) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & + ~ImGuiButtonFlags_PressedOnClickRelease; + } else { + if (window != g.HoveredWindow || !is_mouse_x_over_arrow) + button_flags |= ImGuiButtonFlags_NoKeyModifiers; + } + bool hovered, held; bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); bool toggled = false; if (!is_leaf) { if (pressed && g.DragDropHoldJustPressedId != id) { - if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == - 0 || - (g.NavActivateId == id)) - toggled = true; + if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || + (g.NavActivateId == id && !is_multi_select)) + toggled = true; // Single click if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() // since ButtonBehavior() already did the job if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) - toggled = true; + toggled = true; // Double click } else if (pressed && g.DragDropHoldJustPressedId == id) { IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted // after opening, but never close it again. toggled = true; + else + pressed = false; // Cancel press so it doesn't trigger selection. } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { @@ -750,80 +792,96 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l if (toggled) { is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); + window->DC.StateStorage->SetInt(storage_id, is_open); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } - // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. - if (selected != was_selected) //-V547 + // Multi-selection support (footer) + if (is_multi_select) { + bool pressed_copy = pressed && !toggled; + MultiSelectItemFooter(id, &selected, &pressed_copy); + if (pressed) + SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb); + } + + if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render - const ImU32 text_col = GetColorU32(ImGuiCol_Text); - ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; - if (display_frame) { - // Framed type - const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive - : hovered ? ImGuiCol_HeaderHovered - : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, nav_highlight_flags); - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(window->DrawList, - ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), - text_col); - else if (!is_leaf) - RenderArrow(window->DrawList, - ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, - is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up - : ImGuiDir_Down) - : ImGuiDir_Right, - 1.0f); - else // Leaf without bullet, left-adjusted text - text_pos.x -= text_offset_x - padding.x; - if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) - frame_bb.Max.x -= g.FontSize + style.FramePadding.x; - - if (g.LogEnabled) - LogSetNextTextDecoration("###", "###"); - } else { - // Unframed typed for tree nodes - if (hovered || selected || focused) { - const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive - : hovered || focused ? ImGuiCol_HeaderHovered - : ImGuiCol_Header); - RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + { + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; + if (is_multi_select) + nav_highlight_flags |= + ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle + if (display_frame) { + // Framed type + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive + : hovered ? ImGuiCol_HeaderHovered + : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet( + window->DrawList, + ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), + text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, + ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, + is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up + : ImGuiDir_Down) + : ImGuiDir_Right, + 1.0f); + else // Leaf without bullet, left-adjusted text + text_pos.x -= text_offset_x - padding.x; + if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) + frame_bb.Max.x -= g.FontSize + style.FramePadding.x; + if (g.LogEnabled) + LogSetNextTextDecoration("###", "###"); + } else { + // Unframed typed for tree nodes + if (hovered || selected || focused) { + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive + : hovered || focused ? ImGuiCol_HeaderHovered + : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet( + window->DrawList, + ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), + text_col); + else if (!is_leaf) + RenderArrow( + window->DrawList, + ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), + text_col, + is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up + : ImGuiDir_Down) + : ImGuiDir_Right, + 0.70f); + if (g.LogEnabled) + LogSetNextTextDecoration(">", NULL); } - RenderNavHighlight(frame_bb, id, nav_highlight_flags); - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(window->DrawList, - ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), - text_col); - else if (!is_leaf) - RenderArrow( - window->DrawList, - ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), - text_col, - is_open - ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) - : ImGuiDir_Right, - 0.70f); - if (g.LogEnabled) - LogSetNextTextDecoration(">", NULL); - } - if (span_all_columns) - TablePopBackgroundChannel(); + if (span_all_columns) + TablePopBackgroundChannel(); - // Label - if (display_frame) - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - else - RenderText(text_pos, label, label_end, false); + // Label + if (display_frame) + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + else + RenderText(text_pos, label, label_end, false); + } + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushOverrideID(id); + TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | @@ -833,25 +891,30 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *label, const char *label_end, bool focused, bool *visible) { - ImGuiWindow *window = ImGui::GetCurrentWindow(); + ImGuiWindow *window = GetCurrentWindow(); if (window->SkipItems) return false; - const float row_y = ImGui::GetCursorPosY(); - ImGuiContext &g = *GImGui; const ImGuiStyle &style = g.Style; const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) - ? style.FramePadding - : ImVec2(style.FramePadding.x, - ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); - padding.y *= 1.2f; + const ImVec2 padding = + (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) + ? style.FramePadding + : ImVec2(style.FramePadding.x, + ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); if (!label_end) - label_end = ImGui::FindRenderedTextEnd(label); - const ImVec2 label_size = ImGui::CalcTextSize(label, label_end, false); - const ImVec2 eye_size = ImGui::CalcTextSize(ICON_FK_EYE, nullptr, false); + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); + const ImVec2 eye_size = CalcTextSize(ICON_FK_EYE, nullptr, false); + + const float text_offset_x = + g.FontSize + + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing + const float text_offset_y = + ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it + const float text_width = g.FontSize + label_size.x + padding.x * 2; // Include collapsing arrow // We vertically grow up to current line height up the typical widget height. const float frame_height = @@ -864,87 +927,81 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; frame_bb.Min.y = window->DC.CursorPos.y; - frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; + frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x + : (flags & ImGuiTreeNodeFlags_SpanTextWidth) + ? window->DC.CursorPos.x + text_width + padding.x + : window->WorkRect.Max.x; frame_bb.Max.y = window->DC.CursorPos.y + frame_height; if (display_frame) { - // Framed header expand a little outside the default padding, to the edge of - // InnerClipRect (FIXME: May remove this at some point and make InnerClipRect align with - // WindowPadding.x instead of WindowPadding.x*0.5f) - frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f); + const float outer_extend = + IM_TRUNC(window->WindowPadding.x * + 0.5f); // Framed header expand a little outside of current limits + frame_bb.Min.x -= outer_extend; + frame_bb.Max.x += outer_extend; } - const float text_offset_x = - g.FontSize + - (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing - const float text_offset_y = - ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 - : 0.0f); // Include collapsing - ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x + label_size.y * 1.5f, - window->DC.CursorPos.y + text_offset_y); ImVec2 eye_pos(frame_bb.Min.x + padding.x, window->DC.CursorPos.y); - ImGui::ItemSize(ImVec2(text_width, frame_height), padding.y); + ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); + ItemSize(ImVec2(text_width, frame_height), padding.y); ImRect eye_interact_bb = frame_bb; eye_interact_bb.Max.x = frame_bb.Min.x + eye_size.x + style.ItemSpacing.x; // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing ImRect interact_bb = frame_bb; - if (!display_frame && - (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | + if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | + ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) - interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f; - interact_bb.Min.x += eye_size.x + style.TouchExtraPadding.x; + interact_bb.Max.x = + frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f); + + // Compute open and multi-select states before ItemAdd() as it clear NextItem data. + ImGuiID storage_id = (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasStorageID) + ? g.NextItemData.StorageId + : id; + bool is_open = TreeNodeUpdateNextOpen(storage_id, flags); - // Modify ClipRect for the ItemAdd(), faster than doing a - // PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. - const float backup_clip_rect_min_x = window->ClipRect.Min.x; - const float backup_clip_rect_max_x = window->ClipRect.Max.x; + bool is_visible; if (span_all_columns) { - window->ClipRect.Min.x = window->ParentWorkRect.Min.x; - window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + // Modify ClipRect for the ItemAdd(), faster than doing a + // PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + is_visible = ItemAdd(interact_bb, id); + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } else { + is_visible = ItemAdd(interact_bb, id); } - // ImGui::SetActiveID(0, ImGui::GetCurrentWindow()); - ImGuiID eye_interact_id = ImGui::GetID("eye_button##internal"); - ImGui::ItemAdd(eye_interact_bb, eye_interact_id); + ImGuiID eye_interact_id = GetID("eye_button##internal"); + ItemAdd(eye_interact_bb, eye_interact_id); - // Compute open and multi-select states before ItemAdd() as it clear NextItem data. - bool is_open = ImGui::TreeNodeUpdateNextOpen(id, flags); - bool item_add = ImGui::ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; - if (span_all_columns) { - window->ClipRect.Min.x = backup_clip_rect_min_x; - window->ClipRect.Max.x = backup_clip_rect_max_x; - } - // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled: // Store data for the current depth to allow returning to this node from any child item. - // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between - // TreeNode() and TreePop(). It will become tempting to enable - // ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. Currently - // only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, - // easy to increase. - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && - !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && - ImGui::NavMoveRequestButNoResultYet()) { - g.NavTreeNodeStack.resize(g.NavTreeNodeStack.Size + 1); - ImGuiNavTreeNodeData *nav_tree_node_data = &g.NavTreeNodeStack.back(); - nav_tree_node_data->ID = id; - nav_tree_node_data->InFlags = g.LastItemData.InFlags; - nav_tree_node_data->NavRect = g.LastItemData.NavRect; - window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); - } + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() + // and TreePop(). It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by + // default or move it to ImGuiStyle. + bool store_tree_node_stack_data = false; + if (!(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) { + if ((flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && is_open && !g.NavIdIsAlive) + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && + NavMoveRequestButNoResultYet()) + store_tree_node_stack_data = true; + } const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; - if (!item_add) { + if (!is_visible) { + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - ImGui::TreePushOverrideID(id); + TreePushOverrideID(id); IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | @@ -952,8 +1009,11 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l return is_open; } - if (span_all_columns) - ImGui::TablePushBackgroundChannel(); + if (span_all_columns) { + TablePushBackgroundChannel(); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; + g.LastItemData.ClipRect = window->ClipRect; + } const float eye_hit_x1 = eye_pos.x - style.TouchExtraPadding.x; const float eye_hit_x2 = eye_pos.x + (eye_size.x) + style.TouchExtraPadding.x; @@ -982,15 +1042,19 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l // We allow clicking on the arrow section with keyboard modifiers held, in order to easily // allow browsing a tree while preserving selection with code implementing multi-selection - // patterns. When clicking on the rest of the tree node we always disallow keyboard - // modifiers. + // patterns. When clicking on the rest of the tree node we always disallow keyboard modifiers. const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x; const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x; const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2); - if (window != g.HoveredWindow || !is_mouse_x_over_arrow) - button_flags |= ImGuiButtonFlags_NoKeyModifiers; + + const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0; + if (is_multi_select) // We absolutely need to distinguish open vs select so _OpenOnArrow comes + // by default + flags |= (flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 + ? ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick + : ImGuiTreeNodeFlags_OpenOnArrow; // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags. // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to @@ -1000,9 +1064,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1) // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1) // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and - // _OpenOnArrow=0) It is rather standard that arrow click react on Down rather than Up. We - // set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item - // to be active on the initial MouseDown in order for drag and drop to work. + // _OpenOnArrow=0) It is rather standard that arrow click react on Down rather than Up. We set + // ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be + // active on the initial MouseDown in order for drag and drop to work. if (is_mouse_x_over_arrow) button_flags |= ImGuiButtonFlags_PressedOnClick; else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) @@ -1014,138 +1078,152 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char *l bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0; const bool was_selected = selected; + // Multi-selection support (header) + if (is_multi_select) { + // Handle multi-select + alter button flags for it + MultiSelectItemHeader(id, &selected, &button_flags); + if (is_mouse_x_over_arrow) + button_flags = (button_flags | ImGuiButtonFlags_PressedOnClick) & + ~ImGuiButtonFlags_PressedOnClickRelease; + } else { + if (window != g.HoveredWindow || !is_mouse_x_over_arrow) + button_flags |= ImGuiButtonFlags_NoKeyModifiers; + } + bool hovered, held; - bool pressed = ImGui::ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); bool toggled = false; if (!is_leaf) { if (pressed && g.DragDropHoldJustPressedId != id) { - if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == - 0 || - (g.NavActivateId == id)) - toggled = true; + if ((flags & ImGuiTreeNodeFlags_OpenOnMask_) == 0 || + (g.NavActivateId == id && !is_multi_select)) + toggled = true; // Single click if (flags & ImGuiTreeNodeFlags_OpenOnArrow) toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() // since ButtonBehavior() already did the job if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2) - toggled = true; + toggled = true; // Double click } else if (pressed && g.DragDropHoldJustPressedId == id) { IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold); - if (!is_open) // When using Drag and Drop "hold to open" we keep the node - // highlighted after opening, but never close it again. + if (!is_open) // When using Drag and Drop "hold to open" we keep the node highlighted + // after opening, but never close it again. toggled = true; + else + pressed = false; // Cancel press so it doesn't trigger selection. } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; - ImGui::NavClearPreferredPosForAxis(ImGuiAxis_X); - ImGui::NavMoveRequestCancel(); + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavMoveRequestCancel(); } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the // priority? { toggled = true; - ImGui::NavClearPreferredPosForAxis(ImGuiAxis_X); - ImGui::NavMoveRequestCancel(); + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavMoveRequestCancel(); } if (toggled) { is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); + window->DC.StateStorage->SetInt(storage_id, is_open); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } - // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never - // trigger. - if (selected != was_selected) //-V547 + // Multi-selection support (footer) + if (is_multi_select) { + bool pressed_copy = pressed && !toggled; + MultiSelectItemFooter(id, &selected, &pressed_copy); + if (pressed) + SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, interact_bb); + } + + if (selected != was_selected) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render - const ImU32 text_col = ImGui::GetColorU32(ImGuiCol_Text); - ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; - if (display_frame) { - // Framed type - const ImU32 bg_col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_HeaderActive - : hovered ? ImGuiCol_HeaderHovered - : ImGuiCol_Header); - ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); - ImGui::RenderNavHighlight(frame_bb, id, nav_highlight_flags); - - if (flags & ImGuiTreeNodeFlags_Bullet) - ImGui::RenderBullet( - window->DrawList, - ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), - text_col); - else if (!is_leaf) - ImGui::RenderArrow( - window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), - text_col, - is_open - ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) - : ImGuiDir_Right, - 1.0f); - else // Leaf without bullet, left-adjusted text - text_pos.x -= text_offset_x - padding.x; - if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) - frame_bb.Max.x -= g.FontSize + style.FramePadding.x; - - if (g.LogEnabled) - ImGui::LogSetNextTextDecoration("###", "###"); - } else { - // Unframed typed for tree nodes - if (hovered || selected || focused) { - const ImU32 bg_col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_HeaderActive - : hovered || focused ? ImGuiCol_HeaderHovered - : ImGuiCol_Header); - ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + { + const ImU32 text_col = GetColorU32(ImGuiCol_Text); + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; + if (is_multi_select) + nav_highlight_flags |= + ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle + if (display_frame) { + // Framed type + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive + : hovered ? ImGuiCol_HeaderHovered + : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet( + window->DrawList, + ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), + text_col); + else if (!is_leaf) + RenderArrow(window->DrawList, + ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, + is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up + : ImGuiDir_Down) + : ImGuiDir_Right, + 1.0f); + else // Leaf without bullet, left-adjusted text + text_pos.x -= text_offset_x - padding.x; + if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) + frame_bb.Max.x -= g.FontSize + style.FramePadding.x; + if (g.LogEnabled) + LogSetNextTextDecoration("###", "###"); + } else { + // Unframed typed for tree nodes + if (hovered || selected || focused) { + const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive + : hovered || focused ? ImGuiCol_HeaderHovered + : ImGuiCol_Header); + RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false); + } + RenderNavHighlight(frame_bb, id, nav_highlight_flags); + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet( + window->DrawList, + ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), + text_col); + else if (!is_leaf) + RenderArrow( + window->DrawList, + ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), + text_col, + is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up + : ImGuiDir_Down) + : ImGuiDir_Right, + 0.70f); + if (g.LogEnabled) + LogSetNextTextDecoration(">", NULL); } - ImGui::RenderNavHighlight(frame_bb, id, nav_highlight_flags); - - if (flags & ImGuiTreeNodeFlags_Bullet) - ImGui::RenderBullet( - window->DrawList, - ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), - text_col); - else if (!is_leaf) - ImGui::RenderArrow( - window->DrawList, - ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), - text_col, - is_open - ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) - : ImGuiDir_Right, - 0.70f); - if (g.LogEnabled) - ImGui::LogSetNextTextDecoration(">", NULL); - } - - if (span_all_columns) - ImGui::TablePopBackgroundChannel(); - // Label - if (display_frame) - ImGui::RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - else - ImGui::RenderText(text_pos, label, label_end, false); - - ImGui::PushStyleColor(ImGuiCol_Button, {0, 0, 0, 0}); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, {0, 0, 0, 0}); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, {0, 0, 0, 0}); + if (span_all_columns) + TablePopBackgroundChannel(); - ImGui::RenderText(eye_pos, *visible ? ICON_FK_EYE : ICON_FK_EYE_SLASH, nullptr, false); - - ImGui::PopStyleColor(3); + // Label + if (display_frame) + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + else + RenderText(text_pos, label, label_end, false); + } + if (store_tree_node_stack_data && is_open) + TreeNodeStoreStackData(flags); // Call before TreePushOverrideID() if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - ImGui::TreePushOverrideID(id); + TreePushOverrideID(id); // Could use TreePush(label) but this avoid computing twice + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); - return is_open; } @@ -1329,8 +1407,8 @@ bool ImGui::IsDragDropSource(ImGuiDragDropFlags flags) { return source_drag_active; } - -void ImGui::RenderDragDropTargetRect(const ImRect &bb, const ImRect &item_clip_rect, ImGuiDropFlags flags) { +void ImGui::RenderDragDropTargetRect(const ImRect &bb, const ImRect &item_clip_rect, + ImGuiDropFlags flags) { ImGuiContext &g = *GImGui; ImGuiWindow *window = g.CurrentWindow; ImRect bb_display = bb; @@ -1343,7 +1421,8 @@ void ImGui::RenderDragDropTargetRect(const ImRect &bb, const ImRect &item_clip_r if (flags == ImGuiDropFlags_None || flags == ImGuiDropFlags_InsertChild) { window->DrawList->AddRect(bb_display.Min, bb_display.Max, - GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ImDrawFlags_None, 2.0f); + GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ImDrawFlags_None, + 2.0f); } else if (flags == ImGuiDropFlags_InsertBefore) { float y = bb_display.Min.y; window->DrawList->AddLine(ImVec2(bb_display.Min.x, y), ImVec2(bb_display.Max.x, y), From f5d86bd1ecb8141011ce64b85c62a06b8a372835 Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Wed, 25 Sep 2024 12:06:19 -0500 Subject: [PATCH 5/9] Compiles properly now --- include/resource/resource.hpp | 10 +++++++++- include/serial.hpp | 2 -- src/resource/resource.cpp | 14 +++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/resource/resource.hpp b/include/resource/resource.hpp index e0cdd28f..b3ae3832 100644 --- a/include/resource/resource.hpp +++ b/include/resource/resource.hpp @@ -26,6 +26,12 @@ namespace Toolbox { [[nodiscard]] UUID64 getUUID() const { return m_uuid; } + ResourcePath &operator=(const ResourcePath &other) { + m_path = other.m_path; + m_uuid = other.m_uuid; + return *this; + } + private: UUID64 m_uuid; fs_path m_path; @@ -60,7 +66,8 @@ namespace Toolbox { [[nodiscard]] bool hasResourcePath(fs_path &&path) const; [[nodiscard]] bool hasResourcePath(const UUID64 &path_uuid) const; - [[nodiscard]] bool hasDataPath(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] bool hasDataPath(const fs_path &path, + const UUID64 &resource_path_uuid = 0) const; [[nodiscard]] bool hasDataPath(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; [[nodiscard]] Result, FSError> @@ -83,6 +90,7 @@ namespace Toolbox { std::optional findResourcePath(const fs_path &sub_path) const; void preloadData(const fs_path &resource_path) const; + private: UUID64 m_uuid; std::vector m_resource_paths; diff --git a/include/serial.hpp b/include/serial.hpp index b780c650..24b35abb 100644 --- a/include/serial.hpp +++ b/include/serial.hpp @@ -191,8 +191,6 @@ namespace Toolbox { Deserializer(std::streambuf *in) : m_in(in) {} Deserializer(std::streambuf *in, std::string_view file_path) : m_in(in), m_file_path(file_path) {} - Deserializer(std::fstream &&in, std::string_view file_path) - : m_in(in), m_file_path(file_path) {} Deserializer(const Deserializer &) = default; Deserializer(Deserializer &&) = default; diff --git a/src/resource/resource.cpp b/src/resource/resource.cpp index 0e48b860..b5dfa461 100644 --- a/src/resource/resource.cpp +++ b/src/resource/resource.cpp @@ -147,7 +147,7 @@ namespace Toolbox { } if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { - const ResourceData &data = m_data_preload_cache[path]; + const ResourceData &data = m_data_preload_cache[path]; std::span data_span(static_cast(data.m_data_ptr), data.m_data_size); RefPtr handle = make_referable(data_span); @@ -261,8 +261,6 @@ namespace Toolbox { return make_fs_error>(std::error_code(), {"Resource path not found"}); } - fs_path resource_path; - fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; @@ -306,8 +304,6 @@ namespace Toolbox { return make_fs_error>(std::error_code(), {"Resource path not found"}); } - fs_path resource_path; - fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; @@ -388,8 +384,8 @@ namespace Toolbox { void ResourceManager::preloadData(const fs_path &resource_path) const { std::for_each( - std::execution::par_unseq, Filesystem::directory_iterator(resource_path), - Filesystem::directory_iterator(), [this](const fs_path &file) { + Filesystem::directory_iterator(resource_path), Filesystem::directory_iterator(), + [this](const fs_path &file) { Filesystem::is_regular_file(file).and_then([this, &file](bool is_regular_file) { if (!is_regular_file) { return Result(); @@ -402,7 +398,7 @@ namespace Toolbox { void *buffer = std::malloc(file_size); if (!buffer) { - return make_fs_error( + return make_fs_error( std::error_code(), {"Failed to allocate memory for file buffer"}); } @@ -412,7 +408,7 @@ namespace Toolbox { m_data_preload_cache[file] = {file_size, buffer}; - return Result(); + return Result(); }); return Result(); From 7fd5905fcf94f9ff042eecbb099d50e790b2802e Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Thu, 26 Sep 2024 07:40:49 -0500 Subject: [PATCH 6/9] It compiles and mostly kinda works --- include/dolphin/hook.hpp | 5 ++ include/dolphin/process.hpp | 18 +++---- include/game/task.hpp | 2 + include/gui/application.hpp | 16 ++++-- include/gui/font.hpp | 23 ++++---- include/gui/pad/window.hpp | 2 +- include/gui/scene/camera.hpp | 9 +++- include/gui/scene/objdialog.hpp | 3 ++ include/gui/scene/renderer.hpp | 22 ++++++++ include/gui/settings.hpp | 2 - include/gui/themes.hpp | 8 +-- include/objlib/template.hpp | 3 ++ src/dolphin/hook.cpp | 44 +++++++--------- src/dolphin/process.cpp | 6 +-- src/game/task.cpp | 25 ++++----- src/gui/application.cpp | 93 ++++++++++++++++++++------------- src/gui/camera.cpp | 28 +++++++--- src/gui/font.cpp | 17 ++---- src/gui/logging/logger.cpp | 8 --- src/gui/logging/window.cpp | 16 ++++-- src/gui/pad/recorder.cpp | 3 +- src/gui/pad/window.cpp | 25 ++++++--- src/gui/scene/objdialog.cpp | 3 +- src/gui/scene/renderer.cpp | 9 ++-- src/gui/scene/window.cpp | 6 ++- src/gui/settings.cpp | 4 -- src/gui/settings/window.cpp | 18 ++++--- src/gui/themes.cpp | 29 +++++++--- src/gui/window.cpp | 11 ++-- src/objlib/template.cpp | 16 +++--- src/resource/resource.cpp | 9 +++- 31 files changed, 291 insertions(+), 192 deletions(-) diff --git a/include/dolphin/hook.hpp b/include/dolphin/hook.hpp index 7c2ce44c..7af46657 100644 --- a/include/dolphin/hook.hpp +++ b/include/dolphin/hook.hpp @@ -36,6 +36,9 @@ namespace Toolbox::Dolphin { DolphinHookManager &operator=(DolphinHookManager &&) = delete; public: + fs_path getDolphinPath() const { return m_dolphin_path; } + void setDolphinPath(const fs_path &path) { m_dolphin_path = path; } + // Check if a specific UUID owns the lock bool hasLock(const UUID64 &uuid) const { return m_owner.has_value() && m_owner.value() == uuid; @@ -84,6 +87,8 @@ namespace Toolbox::Dolphin { int xfb_height); private: + fs_path m_dolphin_path; + Platform::ProcessInformation m_proc_info; Platform::MemHandle m_mem_handle{}; diff --git a/include/dolphin/process.hpp b/include/dolphin/process.hpp index 31f19d97..9f5cdd6c 100644 --- a/include/dolphin/process.hpp +++ b/include/dolphin/process.hpp @@ -6,26 +6,17 @@ #include #include "core/core.hpp" -#include "core/types.hpp" +#include "core/error.hpp" #include "core/threaded.hpp" +#include "core/types.hpp" #include "dolphin/hook.hpp" -#include "gui/logging/errors.hpp" -#include "gui/settings.hpp" - using namespace Toolbox; using namespace Toolbox::UI; namespace Toolbox::Dolphin { - inline std::chrono::milliseconds GetSleepDuration(f32 framerate) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - return std::min( - std::chrono::milliseconds(settings.m_dolphin_refresh_rate), - std::chrono::milliseconds(static_cast(1000 / framerate))); - } - class DolphinCommunicator : public Threaded { public: DolphinCommunicator() = default; @@ -34,6 +25,9 @@ namespace Toolbox::Dolphin { void signalHook() { m_hook_flag.store(true); } + s64 getRefreshRate() const { return m_refresh_rate; } + void setRefreshRate(s64 milliseconds) { m_refresh_rate = milliseconds; } + template Result read(u32 address) { T data; @@ -74,6 +68,8 @@ namespace Toolbox::Dolphin { std::thread m_thread; std::atomic m_hook_flag; + + s64 m_refresh_rate = 100; }; } // namespace Toolbox::Dolphin \ No newline at end of file diff --git a/include/game/task.hpp b/include/game/task.hpp index 7a0a1ef0..a94cc256 100644 --- a/include/game/task.hpp +++ b/include/game/task.hpp @@ -13,7 +13,9 @@ #include "dolphin/process.hpp" #include "gui/scene/camera.hpp" #include "objlib/object.hpp" + using namespace Toolbox::Dolphin; +using namespace Toolbox::Object; using namespace Toolbox; diff --git a/include/gui/application.hpp b/include/gui/application.hpp index 6e8f9653..ccd43141 100644 --- a/include/gui/application.hpp +++ b/include/gui/application.hpp @@ -22,7 +22,11 @@ #include "core/clipboard.hpp" #include "dolphin/process.hpp" +#include "gui/font.hpp" +#include "gui/settings.hpp" +#include "resource/resource.hpp" #include "scene/layout.hpp" +#include "themes.hpp" #include using namespace Toolbox::Dolphin; @@ -48,9 +52,6 @@ namespace Toolbox { virtual void onUpdate(TimeStep delta_time) override; virtual void onExit() override; - Toolbox::fs_path getResourcePath(const Toolbox::fs_path &path) const &; - Toolbox::fs_path getResourcePath(Toolbox::fs_path &&path) const &&; - void addWindow(RefPtr window) { addLayer(window); m_windows.push_back(window); @@ -118,6 +119,10 @@ namespace Toolbox { Game::TaskCommunicator &getTaskCommunicator() { return m_task_communicator; } std::filesystem::path getProjectRoot() const { return m_project_root; } + ResourceManager &getResourceManager() { return m_resource_manager; } + ThemeManager &getThemeManager() { return m_theme_manager; } + SettingsManager &getSettingsManager() { return m_settings_manager; } + FontManager &getFontManager() { return m_font_manager; } void registerDolphinOverlay(UUID64 scene_uuid, const std::string &name, SceneWindow::render_layer_cb cb); @@ -160,6 +165,11 @@ namespace Toolbox { std::filesystem::path m_save_path = std::filesystem::current_path(); ScopePtr m_scene_layout_manager; + ResourceManager m_resource_manager; + FontManager m_font_manager; + ThemeManager m_theme_manager; + SettingsManager m_settings_manager; + TemplateFactory m_template_factory; GLFWwindow *m_render_window; std::vector> m_windows; diff --git a/include/gui/font.hpp b/include/gui/font.hpp index 52a7effd..aa751960 100644 --- a/include/gui/font.hpp +++ b/include/gui/font.hpp @@ -15,11 +15,11 @@ namespace Toolbox::UI { class FontManager { + std::string m_current_font_family; + float m_current_font_size; std::multimap m_loaded_fonts; public: - static FontManager &instance(); - std::set fontSizes() { static std::set s_font_sizes = {12.0f, 16.0f, 24.0f}; return s_font_sizes; @@ -40,19 +40,18 @@ namespace Toolbox::UI { const ImWchar *glyph_ranges); void finalize(); - ImFont *getFont(std::string_view name, float size); + ImFont *getFont(std::string_view name, float size) const; - ImFont *getCurrentFont() { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - return getFont(settings.m_font_family, settings.m_font_size); + ImFont *getCurrentFont() const { + return getFont(m_current_font_family, m_current_font_size); } - std::string getCurrentFontFamily() { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - return settings.m_font_family; + + std::string getCurrentFontFamily() const { + return m_current_font_family; } - float getCurrentFontSize() { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - return settings.m_font_size; + + float getCurrentFontSize() const { + return m_current_font_size; } void setCurrentFont(std::string_view name, float size); diff --git a/include/gui/pad/window.hpp b/include/gui/pad/window.hpp index 12596537..5f48e7d0 100644 --- a/include/gui/pad/window.hpp +++ b/include/gui/pad/window.hpp @@ -153,7 +153,7 @@ namespace Toolbox::UI { CreateLinkDialog m_create_link_dialog; - ImageHandle m_dolphin_logo; + RefPtr m_dolphin_logo; ImagePainter m_image_painter; }; diff --git a/include/gui/scene/camera.hpp b/include/gui/scene/camera.hpp index 3dc70cf9..4b81b370 100644 --- a/include/gui/scene/camera.hpp +++ b/include/gui/scene/camera.hpp @@ -31,13 +31,20 @@ namespace Toolbox { void getUp(glm::vec3 &outUp) const; void getLookAt(glm::vec3 &outLookAt) const; void getRight(glm::vec3 &outRight) const; + float getNearDist() const; + float getFarDist() const; + float getFOV() const; + float getAspectRatio() const; void translateLeftRight(float delta); void translateFwdBack(float delta); void tiltUpDown(float ang); void turnLeftRight(float ang); - void setAspect(float aspect); + void setNearDist(float nearDist); + void setFarDist(float farDist); + void setFOV(float FOV); + void setAspectRatio(float aspect); // Why no SETS for Pos, Dir, Up, LookAt and Right? // They have to be adjusted _together_ in setOrientAndPosition() diff --git a/include/gui/scene/objdialog.hpp b/include/gui/scene/objdialog.hpp index 66ccd37e..65b96a58 100644 --- a/include/gui/scene/objdialog.hpp +++ b/include/gui/scene/objdialog.hpp @@ -28,6 +28,7 @@ namespace Toolbox::UI { CreateObjDialog() = default; ~CreateObjDialog() = default; + void setExtendedMode(bool extended) { m_extended_mode = extended; } void setInsertPolicy(InsertPolicy policy) { m_insert_policy = policy; } void setActionOnAccept(action_t on_accept) { m_on_accept = on_accept; } void setActionOnReject(cancel_t on_reject) { m_on_reject = on_reject; } @@ -44,6 +45,8 @@ namespace Toolbox::UI { bool m_open = false; bool m_opening = false; + bool m_extended_mode = false; + int m_template_index = -1; int m_wizard_index = -1; diff --git a/include/gui/scene/renderer.hpp b/include/gui/scene/renderer.hpp index de468347..62471c4c 100644 --- a/include/gui/scene/renderer.hpp +++ b/include/gui/scene/renderer.hpp @@ -44,6 +44,24 @@ namespace Toolbox::UI { m_camera.updateCamera(); } + f32 getCameraFOV() const { return m_camera_fov; } + void setCameraFOV(f32 fov) { + m_camera_fov = fov; + m_camera.setFOV(fov); + } + + f32 getCameraNearPlane() const { return m_camera_near_plane; } + void setCameraNearPlane(f32 near_plane) { + m_camera_near_plane = near_plane; + m_camera.setNearDist(near_plane); + } + + f32 getCameraFarPlane() const { return m_camera_far_plane; } + void setCameraFarPlane(f32 far_plane) { + m_camera_far_plane = far_plane; + m_camera.setFarDist(far_plane); + } + void setGizmoVisible(bool visible) { m_render_gizmo = visible; } bool isGizmoManipulated() const { return m_gizmo_updated; } glm::mat4x4 getGizmoTransform() const { return m_gizmo_matrix; } @@ -93,5 +111,9 @@ namespace Toolbox::UI { ImGuizmo::MODE m_gizmo_mode = ImGuizmo::MODE::WORLD; ImGuizmo::OPERATION m_gizmo_op; glm::mat4x4 m_gizmo_matrix; + + f32 m_camera_fov; + f32 m_camera_near_plane; + f32 m_camera_far_plane; }; } // namespace Toolbox::UI \ No newline at end of file diff --git a/include/gui/settings.hpp b/include/gui/settings.hpp index eba402f0..aae26f1a 100644 --- a/include/gui/settings.hpp +++ b/include/gui/settings.hpp @@ -62,8 +62,6 @@ namespace Toolbox { SettingsManager() = default; - static SettingsManager &instance(); - bool initialize(); bool save(); diff --git a/include/gui/themes.hpp b/include/gui/themes.hpp index 0e104189..ff690123 100644 --- a/include/gui/themes.hpp +++ b/include/gui/themes.hpp @@ -15,17 +15,15 @@ namespace Toolbox::UI { class ITheme { public: - virtual ~ITheme() = default; - virtual std::string_view name() const = 0; virtual bool apply() = 0; }; - class ConfigTheme : public ITheme { + class ConfigTheme final : public ITheme { public: ConfigTheme(std::string_view name); ConfigTheme(std::string_view name, const ImGuiStyle &theme); - ~ConfigTheme() override = default; + ~ConfigTheme() = default; std::string_view name() const override { return m_name; } bool apply() override; @@ -45,8 +43,6 @@ namespace Toolbox::UI { ThemeManager() = default; ~ThemeManager() = default; - static ThemeManager &instance(); - void addTheme(RefPtr theme) { m_themes.push_back(theme); } std::vector> themes() const { return m_themes; } diff --git a/include/objlib/template.hpp b/include/objlib/template.hpp index e8c5e289..46b334bf 100644 --- a/include/objlib/template.hpp +++ b/include/objlib/template.hpp @@ -134,6 +134,9 @@ namespace Toolbox::Object { static Result loadFromCacheBlob(); static Result saveToCacheBlob(); + + static bool isCacheMode(); + static void setCacheMode(bool mode); }; } // namespace Toolbox::Object diff --git a/src/dolphin/hook.cpp b/src/dolphin/hook.cpp index 08148cc4..4ba454ab 100644 --- a/src/dolphin/hook.cpp +++ b/src/dolphin/hook.cpp @@ -10,8 +10,8 @@ #include "dolphin/hook.hpp" #ifdef TOOLBOX_PLATFORM_LINUX -#include #include +#include #include #endif @@ -57,8 +57,7 @@ namespace Toolbox::Dolphin { return pid; } - static Result - OpenProcessMemory(std::string_view memory_name) { + static Result OpenProcessMemory(std::string_view memory_name) { Platform::MemHandle memory_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memory_name.data()); if (!memory_handle) { @@ -80,7 +79,7 @@ namespace Toolbox::Dolphin { } static Result CloseProcessMemory(Platform::MemHandle memory_handle, - const char* dolphin_memory_name) { + const char *dolphin_memory_name) { CloseHandle(memory_handle); return {}; } @@ -105,7 +104,7 @@ namespace Toolbox::Dolphin { if (!pipe) { return make_error("popen() failed!"); } - while(fgets(buffer.data(), static_cast(buffer.size()), pipe.get()) != nullptr) { + while (fgets(buffer.data(), static_cast(buffer.size()), pipe.get()) != nullptr) { pidof_result += buffer.data(); } if (pidof_result.length() == 0) { @@ -114,13 +113,12 @@ namespace Toolbox::Dolphin { return std::stoi(pidof_result); } - static Result - OpenProcessMemory(std::string_view memory_name) { - char* memory_name_owned = new char[1 + memory_name.size() + 1]; - memory_name_owned[0] = '/'; + static Result OpenProcessMemory(std::string_view memory_name) { + char *memory_name_owned = new char[1 + memory_name.size() + 1]; + memory_name_owned[0] = '/'; std::memcpy(memory_name_owned + 1, memory_name.data(), memory_name.size()); memory_name_owned[memory_name.size() + 1] = '\0'; - int fd = shm_open(memory_name_owned, O_RDWR, 0600); + int fd = shm_open(memory_name_owned, O_RDWR, 0600); if (fd == -1) { return make_error( "SHARED_MEMORY", @@ -131,8 +129,7 @@ namespace Toolbox::Dolphin { } static Result OpenMemoryView(Platform::MemHandle memory_handle) { - void *ptr = mmap(NULL, 0x1800000, PROT_READ | PROT_WRITE, MAP_SHARED, - memory_handle, 0); + void *ptr = mmap(NULL, 0x1800000, PROT_READ | PROT_WRITE, MAP_SHARED, memory_handle, 0); if (ptr == MAP_FAILED) { return make_error("SHARED_MEMORY", "Failed to get view of shared process memory handle."); @@ -141,7 +138,7 @@ namespace Toolbox::Dolphin { } static Result CloseProcessMemory(Platform::MemHandle memory_handle, - const char* dolphin_memory_name) { + const char *dolphin_memory_name) { if (shm_unlink(dolphin_memory_name) != 0) { return make_error("SHARED_MEMORY", "Failed to close handle to shared memory object"); @@ -151,8 +148,7 @@ namespace Toolbox::Dolphin { static Result CloseMemoryView(void *memory_view) { if (munmap(memory_view, 0x1800000) != 0) { - return make_error("SHARED_MEMORY", - "Failed to close memory view"); + return make_error("SHARED_MEMORY", "Failed to close memory view"); } return {}; } @@ -168,7 +164,6 @@ namespace Toolbox::Dolphin { } Result DolphinHookManager::startProcess() { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); GUIApplication &application = GUIApplication::instance(); if (isProcessRunning()) { @@ -178,18 +173,17 @@ namespace Toolbox::Dolphin { std::string dolphin_args = std::format("-e {}/sys/main.dol -a HLE", application.getProjectRoot().string()); - std::string dolphin_path = settings.m_dolphin_path.string(); - size_t last_not_null = dolphin_path.find_last_not_of('\000'); - std::string used_substr = dolphin_path.substr(last_not_null - 5, last_not_null); - if (!used_substr.starts_with("-nogui"sv)){ - dolphin_args += "-d -c"; + size_t last_not_null = m_dolphin_path.string().find_last_not_of('\000'); + std::string used_substr = m_dolphin_path.string().substr(last_not_null - 5, last_not_null); + if (!used_substr.starts_with("-nogui"sv)) { + dolphin_args += "-d -c"; } #ifdef TOOLBOX_PLATFORM_LINUX // Force dolphin to run on X11 instead of Wayland if running on Linux putenv("QT_QPA_PLATFORM=xcb"); #endif - auto process_result = Platform::CreateExProcess(settings.m_dolphin_path, dolphin_args); + auto process_result = Platform::CreateExProcess(m_dolphin_path, dolphin_args); if (!process_result) { return std::unexpected(process_result.error()); } @@ -242,8 +236,7 @@ namespace Toolbox::Dolphin { m_proc_info.m_process_id = pid; } - std::string dolphin_memory_name = std::format("dolphin-emu.{}", - m_proc_info.m_process_id); + std::string dolphin_memory_name = std::format("dolphin-emu.{}", m_proc_info.m_process_id); auto handle_result = OpenProcessMemory(dolphin_memory_name); if (!handle_result || handle_result.value() == NULL_MEMHANDLE) { @@ -285,8 +278,7 @@ namespace Toolbox::Dolphin { } m_mem_view = nullptr; - std::string dolphin_memory_name = std::format("dolphin-emu.{}", - m_proc_info.m_process_id); + std::string dolphin_memory_name = std::format("dolphin-emu.{}", m_proc_info.m_process_id); auto handle_result = CloseProcessMemory(m_mem_handle, dolphin_memory_name.data()); if (!handle_result) { return std::unexpected(view_result.error()); diff --git a/src/dolphin/process.cpp b/src/dolphin/process.cpp index 51c0b12b..44967c3a 100644 --- a/src/dolphin/process.cpp +++ b/src/dolphin/process.cpp @@ -12,8 +12,6 @@ #include "dolphin/process.hpp" -#include "gui/settings.hpp" - using namespace Toolbox; using namespace Toolbox::UI; @@ -21,8 +19,6 @@ namespace Toolbox::Dolphin { void DolphinCommunicator::tRun(void *param) { while (!tIsSignalKill()) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - if (!m_hook_flag.load()) { #if 0 std::this_thread::sleep_for(std::chrono::seconds(1)); @@ -36,7 +32,7 @@ namespace Toolbox::Dolphin { m_hook_flag.store(false); } - std::this_thread::sleep_for(std::chrono::milliseconds(settings.m_dolphin_refresh_rate)); + std::this_thread::sleep_for(std::chrono::milliseconds(m_refresh_rate)); } } diff --git a/src/game/task.cpp b/src/game/task.cpp index 2aecf83a..23788fbe 100644 --- a/src/game/task.cpp +++ b/src/game/task.cpp @@ -11,6 +11,7 @@ #include "game/task.hpp" #include "gui/application.hpp" +#include "gui/logging/errors.hpp" #include "gui/settings.hpp" using namespace Toolbox; @@ -87,8 +88,6 @@ namespace Toolbox::Game { m_game_interpreter.setGlobalsPointerRW(0x804141C0); while (!tIsSignalKill()) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - DolphinCommunicator &communicator = GUIApplication::instance().getDolphinCommunicator(); m_game_interpreter.setMemoryBuffer(communicator.manager().getMemoryView(), @@ -103,10 +102,10 @@ namespace Toolbox::Game { m_task_queue.pop(); } std::this_thread::sleep_for( - std::chrono::milliseconds(settings.m_dolphin_refresh_rate)); + std::chrono::milliseconds(communicator.getRefreshRate())); } - std::this_thread::sleep_for(std::chrono::milliseconds(settings.m_dolphin_refresh_rate)); + std::this_thread::sleep_for(std::chrono::milliseconds(communicator.getRefreshRate())); } } @@ -156,18 +155,16 @@ namespace Toolbox::Game { void TaskCommunicator::waitMutex(u32 mutex_ptr) { while (true) { DolphinCommunicator &communicator = GUIApplication::instance().getDolphinCommunicator(); - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); u32 mutex_lock_count = communicator.read(mutex_ptr + 0xC).value(); if (mutex_lock_count == 0) { return; } - std::this_thread::sleep_for(std::chrono::milliseconds(settings.m_dolphin_refresh_rate)); + std::this_thread::sleep_for(std::chrono::milliseconds(communicator.getRefreshRate())); } } void TaskCommunicator::lockMutex(u32 thread_ptr, u32 mutex_ptr) { DolphinCommunicator &communicator = GUIApplication::instance().getDolphinCommunicator(); - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); while (true) { u32 mutex_thread_ptr = communicator.read(mutex_ptr + 0x8).value(); @@ -190,7 +187,7 @@ namespace Toolbox::Game { return; } communicator.write(thread_ptr + 0x2F0, mutex_ptr); - std::this_thread::sleep_for(std::chrono::milliseconds(settings.m_dolphin_refresh_rate)); + std::this_thread::sleep_for(std::chrono::milliseconds(communicator.getRefreshRate())); communicator.write(thread_ptr + 0x2F0, 0); } } @@ -463,13 +460,13 @@ namespace Toolbox::Game { auto game_stage_result = communicator.read(application_addr + 0xE); if (!game_stage_result) { - LogError(game_stage_result.error()); + UI::LogError(game_stage_result.error()); return false; } auto game_scenario_result = communicator.read(application_addr + 0xF); if (!game_scenario_result) { - LogError(game_scenario_result.error()); + UI::LogError(game_scenario_result.error()); return false; } @@ -516,13 +513,13 @@ namespace Toolbox::Game { auto game_stage_result = communicator.read(application_addr + 0xE); if (!game_stage_result) { - LogError(game_stage_result.error()); + UI::LogError(game_stage_result.error()); return false; } auto game_scenario_result = communicator.read(application_addr + 0xF); if (!game_scenario_result) { - LogError(game_scenario_result.error()); + UI::LogError(game_scenario_result.error()); return false; } @@ -549,13 +546,13 @@ namespace Toolbox::Game { auto game_stage_result = communicator.read(application_addr + 0xE); if (!game_stage_result) { - LogError(game_stage_result.error()); + UI::LogError(game_stage_result.error()); return true; } auto game_scenario_result = communicator.read(application_addr + 0xF); if (!game_scenario_result) { - LogError(game_scenario_result.error()); + UI::LogError(game_scenario_result.error()); return true; } diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 7695a76e..01fd36aa 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -78,8 +79,18 @@ namespace Toolbox { NFD_Init(); // TODO: Load application settings - // - // ---- + + // Initialize the resource manager + { + fs_path cwd = Filesystem::current_path().value(); + + m_resource_manager.includeResourcePath(cwd / "Fonts", true); + m_resource_manager.includeResourcePath(cwd / "Images", true); + m_resource_manager.includeResourcePath(cwd / "Images", true); + m_resource_manager.includeResourcePath(cwd / fs_path("Images") / "Icons", true); + m_resource_manager.includeResourcePath(cwd / "Templates", false); + m_resource_manager.includeResourcePath(cwd / "Themes", true); + } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); @@ -156,30 +167,32 @@ namespace Toolbox { platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow_Ex; platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow_Ex; - if (!SettingsManager::instance().initialize()) { + if (!m_settings_manager.initialize()) { TOOLBOX_ERROR("[INIT] Failed to initialize settings manager!"); } - auto &font_manager = FontManager::instance(); - if (!font_manager.initialize()) { + const AppSettings &settings = m_settings_manager.getCurrentProfile(); + + if (!m_font_manager.initialize()) { TOOLBOX_ERROR("[INIT] Failed to initialize font manager!"); } else { - font_manager.setCurrentFont("NotoSansJP-Regular", 16.0f); + m_font_manager.setCurrentFont(settings.m_font_family, settings.m_font_size); } - // glEnable(GL_MULTISAMPLE); - TRY(TemplateFactory::initialize()).error([](const FSError &error) { LogError(error); }); - TRY(ThemeManager::instance().initialize()).error([](const FSError &error) { - LogError(error); - }); + TRY(m_theme_manager.initialize()).error([](const FSError &error) { LogError(error); }); - createWindow("Application Log"); - - determineEnvironmentConflicts(); + DolphinHookManager::instance().setDolphinPath(settings.m_dolphin_path); + m_dolphin_communicator.setRefreshRate(settings.m_dolphin_refresh_rate); m_dolphin_communicator.tStart(false, nullptr); + m_task_communicator.tStart(false, nullptr); + + + createWindow("Application Log"); + + determineEnvironmentConflicts(); } void GUIApplication::onUpdate(TimeStep delta_time) { @@ -211,6 +224,17 @@ namespace Toolbox { Input::PostUpdateInputState(); } + + // Update settings + { + const AppSettings &settings = m_settings_manager.getCurrentProfile(); + + DolphinHookManager::instance().setDolphinPath(settings.m_dolphin_path); + m_dolphin_communicator.setRefreshRate(settings.m_dolphin_refresh_rate); + m_font_manager.setCurrentFont(settings.m_font_family, settings.m_font_size); + + TemplateFactory::setCacheMode(settings.m_is_template_cache_allowed); + } } void GUIApplication::onExit() { @@ -231,14 +255,6 @@ namespace Toolbox { NFD_Quit(); } - Toolbox::fs_path GUIApplication::getResourcePath(const Toolbox::fs_path &path) const & { - return Toolbox::Filesystem::current_path().value_or(".") / path; - } - - Toolbox::fs_path GUIApplication::getResourcePath(Toolbox::fs_path &&path) const && { - return Toolbox::Filesystem::current_path().value_or(".") / std::move(path); - } - RefPtr GUIApplication::findWindow(UUID64 uuid) { auto it = std::find_if(m_windows.begin(), m_windows.end(), [&uuid](const auto &window) { return window->getUUID() == uuid; }); @@ -277,15 +293,20 @@ namespace Toolbox { } void GUIApplication::initializeIcon() { - fs_path res_path = GUIApplication::instance().getResourcePath("Images/Icons/toolbox.png"); - - std::ifstream in(res_path, std::ios::in | std::ios::binary); + auto result = m_resource_manager.getRawData( + "toolbox.png", m_resource_manager.getResourcePathUUID(fs_path("Images") / "Icons")); + if (!result) { + TOOLBOX_ERROR("Failed to load toolbox icon!"); + return; + } - int width, height, channels; + std::span data_span = std::move(result.value()); // Load image data { - stbi_uc *data = stbi_load(res_path.string().c_str(), &width, &height, &channels, 4); + int width, height, channels; + stbi_uc *data = stbi_load_from_memory(data_span.data(), data_span.size(), &width, + &height, &channels, 4); GLFWimage icon = {width, height, data}; glfwSetWindowIcon(m_render_window, 1, &icon); @@ -544,9 +565,8 @@ namespace Toolbox { } } - void FileDialog::openDialog( - std::filesystem::path starting_path, GLFWwindow *parent_window, bool is_directory, - std::optional maybe_filters) { + void FileDialog::openDialog(std::filesystem::path starting_path, GLFWwindow *parent_window, + bool is_directory, std::optional maybe_filters) { if (m_thread_initialized) { m_thread.join(); } else { @@ -562,14 +582,14 @@ namespace Toolbox { NFD_GetNativeWindowFromGLFWWindow(parent_window, &args.parentWindow); m_result = NFD_PickFolderU8_With(&m_selected_path, &args); } else { - int num_filters = 0; + int num_filters = 0; nfdu8filteritem_t *nfd_filters = nullptr; if (maybe_filters) { auto filters = maybe_filters.value(); - num_filters = filters.numFilters(); + num_filters = filters.numFilters(); filters.copyFiltersOutU8(m_filters); nfd_filters = new nfdu8filteritem_t[num_filters]; - for (int i = 0; i < filters.numFilters(); ++i){ + for (int i = 0; i < filters.numFilters(); ++i) { nfd_filters[i] = {m_filters[i].first.c_str(), m_filters[i].second.c_str()}; } } @@ -588,17 +608,18 @@ namespace Toolbox { m_thread = std::thread(fn); } - void FileDialogFilter::addFilter(const std::string &label, const std::string &csv_filters){ + void FileDialogFilter::addFilter(const std::string &label, const std::string &csv_filters) { m_filters.push_back({std::string(label), std::string(csv_filters)}); } bool FileDialogFilter::hasFilter(const std::string &label) const { return std::find_if(m_filters.begin(), m_filters.end(), - [label](std::pair p){ + [label](std::pair p) { return p.first == label; }) != m_filters.end(); } - void FileDialogFilter::copyFiltersOutU8(std::vector> &out_filters) const { + void FileDialogFilter::copyFiltersOutU8( + std::vector> &out_filters) const { out_filters = m_filters; } } // namespace Toolbox diff --git a/src/gui/camera.cpp b/src/gui/camera.cpp index 1545c3c0..05604c3f 100644 --- a/src/gui/camera.cpp +++ b/src/gui/camera.cpp @@ -45,12 +45,12 @@ namespace Toolbox { this->vPos = inPos; }; -// The projection matrix -void Camera::privUpdateProjectionMatrix(void) { - if (aspectRatio <= 0) - return; - this->projMatrix = glm::perspective(fovy, aspectRatio, nearDist, farDist); -}; + // The projection matrix + void Camera::privUpdateProjectionMatrix(void) { + if (aspectRatio <= 0) + return; + this->projMatrix = glm::perspective(fovy, aspectRatio, nearDist, farDist); + }; void Camera::privUpdateViewMatrix(void) { this->viewMatrix = glm::lookAt(vPos, vPos + vDir, vUp); @@ -79,6 +79,14 @@ void Camera::privUpdateProjectionMatrix(void) { void Camera::getRight(glm::vec3 &outRight) const { outRight = this->vRight; } + float Camera::getNearDist() const { return this->nearDist; } + + float Camera::getFarDist() const { return this->farDist; } + + float Camera::getFOV() const { return this->fovy; } + + float Camera::getAspectRatio() const { return this->aspectRatio; } + void Camera::translateLeftRight(float delta) { vPos += vRight * delta; } void Camera::translateFwdBack(float delta) { vPos += vDir * delta; } @@ -97,6 +105,12 @@ void Camera::privUpdateProjectionMatrix(void) { setOrientAndPosition(vUp, vPos - vDir, vPos); } - void Camera::setAspect(float aspect) { this->aspectRatio = aspect; } + void Camera::setNearDist(float nearDist) { this->nearDist = nearDist; } + + void Camera::setFarDist(float farDist) { this->farDist = farDist; } + + void Camera::setAspectRatio(float aspect) { this->aspectRatio = aspect; } + + void Camera::setFOV(float fov) { this->fovy = fov; } } // namespace Toolbox \ No newline at end of file diff --git a/src/gui/font.cpp b/src/gui/font.cpp index db00cdc8..e50fb0d5 100644 --- a/src/gui/font.cpp +++ b/src/gui/font.cpp @@ -1,10 +1,6 @@ #include "gui/font.hpp" namespace Toolbox::UI { - FontManager &FontManager::instance() { - static FontManager _inst; - return _inst; - } bool FontManager::initialize() { auto cwd_result = Toolbox::Filesystem::current_path(); @@ -53,7 +49,7 @@ namespace Toolbox::UI { void FontManager::finalize() { ImGui::GetIO().Fonts->Build(); } - ImFont *FontManager::getFont(std::string_view name, float size) { + ImFont *FontManager::getFont(std::string_view name, float size) const { for (auto font = m_loaded_fonts.find(std::string(name)); font != m_loaded_fonts.end(); font++) { if (font->second->FontSize == size) { @@ -97,21 +93,18 @@ namespace Toolbox::UI { } void FontManager::setCurrentFont(std::string_view name, float size) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - settings.m_font_family = name; - settings.m_font_size = size; + m_current_font_family = name; + m_current_font_size = size; ImGui::GetIO().FontDefault = getCurrentFont(); } void FontManager::setCurrentFontFamily(std::string_view name) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - settings.m_font_family = name; + m_current_font_family = name; ImGui::GetIO().FontDefault = getCurrentFont(); } void FontManager::setCurrentFontSize(float size) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - settings.m_font_size = size; + m_current_font_size = size; ImGui::GetIO().FontDefault = getCurrentFont(); } } // namespace Toolbox::UI \ No newline at end of file diff --git a/src/gui/logging/logger.cpp b/src/gui/logging/logger.cpp index 5365ff85..3681d6cf 100644 --- a/src/gui/logging/logger.cpp +++ b/src/gui/logging/logger.cpp @@ -1,5 +1,4 @@ #include "core/log.hpp" -#include "gui/settings.hpp" namespace Toolbox::Log { AppLogger &AppLogger::instance() { @@ -8,13 +7,6 @@ namespace Toolbox::Log { } void AppLogger::log(ReportLevel level, const std::string &message) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - if (settings.m_log_to_cout_cerr) { - if (level == ReportLevel::REPORT_ERROR) - std::cerr << message << std::endl; - else - std::cout << message << std::endl; - } m_write_mutex.lock(); m_messages.emplace_back(level, message, m_indentation); m_log_callback(m_messages.back()); diff --git a/src/gui/logging/window.cpp b/src/gui/logging/window.cpp index 261c0003..666b6e02 100644 --- a/src/gui/logging/window.cpp +++ b/src/gui/logging/window.cpp @@ -1,5 +1,6 @@ #include +#include "gui/application.hpp" #include "gui/font.hpp" #include "gui/logging/window.hpp" @@ -7,6 +8,14 @@ static std::string s_message_pool; namespace Toolbox::UI { void LoggingWindow::appendMessageToPool(const Log::AppLogger::LogMessage &message) { + AppSettings &settings = GUIApplication::instance().getSettingsManager().getCurrentProfile(); + if (settings.m_log_to_cout_cerr) { + if (message.m_level == Log::ReportLevel::REPORT_ERROR) + std::cerr << message.m_message << std::endl; + else + std::cout << message.m_message << std::endl; + } + switch (message.m_level) { case Log::ReportLevel::REPORT_LOG: s_message_pool += std::format("[LOG] - {:>{}}", message.m_message, @@ -87,7 +96,8 @@ namespace Toolbox::UI { } void LoggingWindow::onRenderBody(TimeStep delta_time) { - ImFont *mono_font = FontManager::instance().getFont("NanumGothicCoding-Bold", 12.0f); + ImFont *mono_font = + GUIApplication::instance().getFontManager().getFont("NanumGothicCoding-Bold", 12.0f); if (mono_font) { ImGui::PushFont(mono_font); } @@ -96,8 +106,8 @@ namespace Toolbox::UI { ImGuiWindowFlags_AlwaysUseWindowPadding)) { bool is_auto_scroll_mode = ImGui::GetScrollMaxY() - ImGui::GetScrollY() < 12.0f; - auto &messages = Log::AppLogger::instance().messages(); - size_t begin = (size_t)std::max( + auto &messages = Log::AppLogger::instance().messages(); + size_t begin = (size_t)std::max( 0, messages.size() - 5000); // Have up to 5000 messages at once displayed for (size_t i = begin; i < messages.size(); ++i) { auto &message = messages[i]; diff --git a/src/gui/pad/recorder.cpp b/src/gui/pad/recorder.cpp index 35773588..14ef82be 100644 --- a/src/gui/pad/recorder.cpp +++ b/src/gui/pad/recorder.cpp @@ -3,6 +3,7 @@ #include "gui/application.hpp" #include "gui/pad/recorder.hpp" +#include "gui/logging/errors.hpp" namespace Toolbox { @@ -316,7 +317,7 @@ namespace Toolbox { return Result(); }) .or_else([](const FSError &error) { - LogError(error); + UI::LogError(error); return Result(); }); diff --git a/src/gui/pad/window.cpp b/src/gui/pad/window.cpp index 39e9a245..840e3472 100644 --- a/src/gui/pad/window.cpp +++ b/src/gui/pad/window.cpp @@ -30,7 +30,7 @@ #include "gui/scene/renderer.hpp" #include "gui/window.hpp" -#include "gui/imgui_ext.hpp" +#include "gui/logging/errors.hpp" #include "gui/stb_image.h" #include @@ -481,8 +481,11 @@ namespace Toolbox::UI { ImVec2 image_pos = ImGui::GetContentRegionAvail() / 2.0f; image_pos.x -= 100.0f; image_pos.y -= 100.0f; - m_image_painter.render(m_dolphin_logo, ImGui::GetWindowPos() + image_pos, - {200.0f, 200.0f}); + + if (m_dolphin_logo) { + m_image_painter.render(*m_dolphin_logo, ImGui::GetWindowPos() + image_pos, + {200.0f, 200.0f}); + } return; } @@ -960,7 +963,7 @@ namespace Toolbox::UI { return Result(true); }) .or_else([](const FSError &error) { - LogError(error); + UI::LogError(error); return Result(false); }); @@ -1449,7 +1452,8 @@ namespace Toolbox::UI { ReplayLinkNode &link_node = link_nodes[from_link - 'A']; for (size_t i = 0; i < 3; ++i) { if (link_node.m_infos[i].m_next_link == to_link) { - TOOLBOX_ERROR_V("[PAD RECORD] Link {} -> {} already exists.", from_link, to_link); + TOOLBOX_ERROR_V("[PAD RECORD] Link {} -> {} already exists.", from_link, + to_link); return; } @@ -1488,7 +1492,7 @@ namespace Toolbox::UI { #else if (to_link >= 'A' + link_nodes.size()) { TOOLBOX_ERROR_V("[PAD RECORD] Link {} -> {} references a future node.", from_link, - to_link); + to_link); } #endif } @@ -1561,11 +1565,18 @@ namespace Toolbox::UI { } void PadInputWindow::onAttach() { + ResourceManager &res_manager = GUIApplication::instance().getResourceManager(); + UUID64 images_dir = res_manager.getResourcePathUUID("Images"); + m_pad_recorder.onCreateLink( [this](const ReplayLinkNode &node) { tryReuseOrCreateRailNode(node); }); m_pad_recorder.tStart(false, nullptr); - m_dolphin_logo = GUIApplication::instance().getResourcePath("Images/dolphin_logo.png"); + auto result = res_manager.getImageHandle("dolphin_logo.png", images_dir); + if (result) { + m_dolphin_logo = result.value(); + } + m_image_painter.setTintColor({0.0f, 0.0f, 0.0f, 0.5f}); setIcon("controller_64.png"); diff --git a/src/gui/scene/objdialog.cpp b/src/gui/scene/objdialog.cpp index 848cf6e4..8cb1ef53 100644 --- a/src/gui/scene/objdialog.cpp +++ b/src/gui/scene/objdialog.cpp @@ -70,8 +70,7 @@ namespace Toolbox::UI { bool is_selected = i == m_template_index; std::string_view template_type = m_templates.at(i)->type(); - const AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - if (!settings.m_is_better_obj_allowed) { + if (!m_extended_mode) { // Selectively remove extended objects for naive user sake bool is_better_object = std::any_of(s_better_sms_objects.begin(), s_better_sms_objects.end(), diff --git a/src/gui/scene/renderer.cpp b/src/gui/scene/renderer.cpp index 40365f19..d0512ad9 100644 --- a/src/gui/scene/renderer.cpp +++ b/src/gui/scene/renderer.cpp @@ -284,9 +284,8 @@ namespace Toolbox::UI { } // namespace Render Renderer::Renderer() { - const AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - m_camera.setPerspective(glm::radians(settings.m_camera_fov), 16 / 9, settings.m_near_plane, - settings.m_far_plane); + m_camera.setPerspective(glm::radians(70.0f), 16.0f / 9.0f, 10.0f, + 100000.0f); m_camera.setOrientAndPosition({0, 1, 0}, {0, 0, 1}, {0, 0, 0}); m_camera.updateCamera(); J3D::Rendering::SetSortFunction(Render::PacketSort); @@ -572,7 +571,8 @@ namespace Toolbox::UI { } bool Renderer::inputUpdate(TimeStep delta_time) { - const AppSettings &settings = SettingsManager::instance().getCurrentProfile(); + const AppSettings &settings = + GUIApplication::instance().getSettingsManager().getCurrentProfile(); if (m_is_view_manipulating && Input::GetMouseButton(Input::MouseButton::BUTTON_RIGHT)) { double delta_x, delta_y; @@ -585,7 +585,6 @@ namespace Toolbox::UI { } if (m_is_window_focused) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); bool translate_held = settings.m_gizmo_translate_mode_keybind.isInputMatching(); bool rotate_held = settings.m_gizmo_rotate_mode_keybind.isInputMatching(); bool scale_held = settings.m_gizmo_scale_mode_keybind.isInputMatching(); diff --git a/src/gui/scene/window.cpp b/src/gui/scene/window.cpp index 63f76841..ae35d344 100644 --- a/src/gui/scene/window.cpp +++ b/src/gui/scene/window.cpp @@ -1210,7 +1210,8 @@ void SceneWindow::renderRailEditor() { } void SceneWindow::renderScene(TimeStep delta_time) { - const AppSettings &settings = SettingsManager::instance().getCurrentProfile(); + const AppSettings &settings = + GUIApplication::instance().getSettingsManager().getCurrentProfile(); std::vector lights; @@ -2136,6 +2137,9 @@ void SceneWindow::buildContextMenuMultiRailNode() { } void SceneWindow::buildCreateObjDialog() { + AppSettings &settings = GUIApplication::instance().getSettingsManager().getCurrentProfile(); + + m_create_obj_dialog.setExtendedMode(settings.m_is_better_obj_allowed); m_create_obj_dialog.setup(); m_create_obj_dialog.setActionOnAccept( [this](size_t sibling_index, std::string_view name, const Object::Template &template_, diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 3c0bcf68..accfddf2 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -9,10 +9,6 @@ #include namespace Toolbox { - SettingsManager &SettingsManager::instance() { - static SettingsManager _inst; - return _inst; - } bool SettingsManager::initialize() { auto cwd_result = Toolbox::Filesystem::current_path(); diff --git a/src/gui/settings/window.cpp b/src/gui/settings/window.cpp index 69a06783..a66a71aa 100644 --- a/src/gui/settings/window.cpp +++ b/src/gui/settings/window.cpp @@ -2,10 +2,12 @@ #include "core/keybind/keybind.hpp" #include "gui/font.hpp" #include "gui/imgui_ext.hpp" +#include "gui/logging/errors.hpp" #include "gui/settings.hpp" #include "gui/themes.hpp" +#include "gui/application.hpp" + #include -#include namespace Toolbox::UI { @@ -88,7 +90,7 @@ namespace Toolbox::UI { } void SettingsWindow::renderProfileBar(TimeStep delta_time) { - SettingsManager &manager = SettingsManager::instance(); + SettingsManager &manager = GUIApplication::instance().getSettingsManager(); ImGui::Text("Current Profile"); @@ -190,13 +192,13 @@ namespace Toolbox::UI { } void SettingsWindow::renderSettingsGeneral(TimeStep delta_time) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); + AppSettings &settings = GUIApplication::instance().getSettingsManager().getCurrentProfile(); ImGui::Checkbox("Include BetterSMS Objects", &settings.m_is_better_obj_allowed); ImGui::Checkbox("Enable File Backup on Save", &settings.m_is_file_backup_allowed); } void SettingsWindow::renderSettingsControl(TimeStep delta_time) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); + AppSettings &settings = GUIApplication::instance().getSettingsManager().getCurrentProfile(); static KeyBind s_gizmo_translate_keybind = {}; static KeyBind s_gizmo_rotate_keybind = {}; @@ -255,7 +257,7 @@ namespace Toolbox::UI { } void SettingsWindow::renderSettingsUI(TimeStep delta_time) { - auto &manager = ThemeManager::instance(); + ThemeManager &manager = GUIApplication::instance().getThemeManager(); auto themes = manager.themes(); @@ -274,7 +276,7 @@ namespace Toolbox::UI { } ImGui::EndCombo(); } - auto &font_manager = FontManager::instance(); + FontManager &font_manager = GUIApplication::instance().getFontManager(); float current_font_size = font_manager.getCurrentFontSize(); std::string current_font_family = font_manager.getCurrentFontFamily(); @@ -301,7 +303,7 @@ namespace Toolbox::UI { } void SettingsWindow::renderSettingsPreview(TimeStep delta_time) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); + AppSettings &settings = GUIApplication::instance().getSettingsManager().getCurrentProfile(); ImGui::Checkbox("Use Simple Rendering", &settings.m_is_rendering_simple); ImGui::Checkbox("Show Origin Point", &settings.m_is_show_origin_point); @@ -327,7 +329,7 @@ namespace Toolbox::UI { } void SettingsWindow::renderSettingsAdvanced(TimeStep delta_time) { - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); + AppSettings &settings = GUIApplication::instance().getSettingsManager().getCurrentProfile(); if (ImGui::BeginGroupPanel("Dolphin Integration", nullptr, {})) { s64 min = 1; diff --git a/src/gui/themes.cpp b/src/gui/themes.cpp index b4f392dd..48e08f52 100644 --- a/src/gui/themes.cpp +++ b/src/gui/themes.cpp @@ -1,6 +1,7 @@ #include #include "fsystem.hpp" +#include "gui/application.hpp" #include "gui/themes.hpp" namespace Toolbox::UI { @@ -39,13 +40,27 @@ namespace Toolbox::UI { out.write(reinterpret_cast(&m_style), sizeof(m_style)); } - ThemeManager &ThemeManager::instance() { - static ThemeManager instance_; - return instance_; - } - Result ThemeManager::initialize() { - return Toolbox::Filesystem::current_path().and_then([this](std::filesystem::path &&cwd) { + const ResourceManager &manager = GUIApplication::instance().getResourceManager(); + UUID64 themes_uuid = ResourceManager::getResourcePathUUID("Themes"); + if (!manager.hasResourcePath(themes_uuid)) { + return make_fs_error(std::error_code(), {"Themes not found"}); + } + + for (auto &subpath : Toolbox::Filesystem::directory_iterator{"Themes"}) { + try { + auto theme = make_referable(subpath.path().stem().string()); + if (theme->name() == "Default") { + theme->apply(); + m_active_theme = m_themes.size(); + } + m_themes.emplace_back(theme); + } catch (std::runtime_error &e) { + return make_fs_error(std::error_code(), {e.what()}); + } + } + + /*return Toolbox::Filesystem::current_path().and_then([this](std::filesystem::path &&cwd) { for (auto &subpath : Toolbox::Filesystem::directory_iterator{cwd / "Themes"}) { try { auto theme = make_referable(subpath.path().stem().string()); @@ -59,7 +74,7 @@ namespace Toolbox::UI { } } return Result(); - }); + });*/ } } // namespace Toolbox::UI \ No newline at end of file diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 744df55a..adb1df16 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -28,16 +28,21 @@ namespace Toolbox::UI { } void ImWindow::setIcon(const std::string &icon_name) { - fs_path res_path = GUIApplication::instance().getResourcePath("Images/Icons/" + icon_name); + ResourceManager &res_manager = GUIApplication::instance().getResourceManager(); + UUID64 icon_dir = res_manager.getResourcePathUUID(fs_path("Images") / "Icons"); - std::ifstream in(res_path, std::ios::in | std::ios::binary); + auto result = res_manager.getRawData(icon_name, icon_dir); + if (!result) { + return; + } Buffer data_buf; int width, height, channels; // Load image data { - stbi_uc *data = stbi_load(res_path.string().c_str(), &width, &height, &channels, 4); + stbi_uc *data = + stbi_load_from_memory(result.value().data(), result.value().size(), &width, &height, &channels, 4); data_buf.alloc(static_cast(width * height * channels)); std::memcpy(data_buf.buf(), data, data_buf.size()); diff --git a/src/objlib/template.cpp b/src/objlib/template.cpp index 4705673d..172fccaa 100644 --- a/src/objlib/template.cpp +++ b/src/objlib/template.cpp @@ -454,10 +454,8 @@ namespace Toolbox::Object { return std::unexpected(cwd_result.error()); } - AppSettings &settings = SettingsManager::instance().getCurrentProfile(); - bool templates_preloaded = false; - if (settings.m_is_template_cache_allowed) { + if (isCacheMode()) { templates_preloaded = loadFromCacheBlob().has_value(); } @@ -473,10 +471,10 @@ namespace Toolbox::Object { for (auto &th : threads) { th.join(); } - } - if (settings.m_is_template_cache_allowed && !templates_preloaded) { - return saveToCacheBlob(); + if (isCacheMode()) { + return saveToCacheBlob(); + } } return {}; @@ -589,6 +587,12 @@ namespace Toolbox::Object { return {}; } + static bool s_cache_mode = false; + + bool TemplateFactory::isCacheMode() { return s_cache_mode; } + + void TemplateFactory::setCacheMode(bool mode) { s_cache_mode = mode; } + TemplateFactory::create_t TemplateFactory::create(std::string_view type) { auto type_str = std::string(type); if (g_template_cache.contains(type_str)) { diff --git a/src/resource/resource.cpp b/src/resource/resource.cpp index b5dfa461..fe698d6c 100644 --- a/src/resource/resource.cpp +++ b/src/resource/resource.cpp @@ -375,11 +375,18 @@ namespace Toolbox { } UUID64 ResourceManager::getResourcePathUUID(const fs_path &path) { + if (!path.is_absolute()) { + fs_path abs_path = Filesystem::weakly_canonical(path).value_or(path); + return UUID64(std::hash{}(abs_path)); + } return UUID64(std::hash{}(path)); } UUID64 ResourceManager::getResourcePathUUID(fs_path &&path) { - return UUID64(std::hash{}(std::move(path))); + if (!path.is_absolute()) { + path = Filesystem::weakly_canonical(path).value_or(path); + } + return UUID64(std::hash{}(path)); } void ResourceManager::preloadData(const fs_path &resource_path) const { From 1968cc4fc248349496cb176eb7eb8c27b2da2b03 Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Thu, 26 Sep 2024 15:38:59 -0500 Subject: [PATCH 7/9] Refactor more code --- Themes/Basic Dark.theme | Bin 1132 -> 0 bytes Themes/Basic Light.theme | Bin 1132 -> 0 bytes Themes/Default.theme | Bin 1132 -> 0 bytes Themes/Moonshine.theme | Bin 1132 -> 0 bytes Themes/Sunshine.theme | Bin 1132 -> 0 bytes include/gui/settings.hpp | 2 +- include/gui/themes.hpp | 14 ++- include/resource/resource.hpp | 20 ++- src/gui/settings/window.cpp | 20 +-- src/gui/themes.cpp | 224 ++++++++++++++++++++++++++++++---- src/gui/window.cpp | 2 +- src/resource/resource.cpp | 80 +++++++----- 12 files changed, 290 insertions(+), 72 deletions(-) delete mode 100644 Themes/Basic Dark.theme delete mode 100644 Themes/Basic Light.theme delete mode 100644 Themes/Default.theme delete mode 100644 Themes/Moonshine.theme delete mode 100644 Themes/Sunshine.theme diff --git a/Themes/Basic Dark.theme b/Themes/Basic Dark.theme deleted file mode 100644 index 24885d8f529886a17989b8722c12701c7ee491ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1132 zcma)6D@+4H5Z&Vw5)2hh2%#wg(*${ybmst31PWb2O*ILrL7gcUf@(=1Bt;O!8B7%+ zHJGbF)nL&0YUb_jaC>`(OzVy`)@RHWM#R99_)M{;mu`)MOro|^KZmf6Mz44i23W8w9 zG5o%fhIWyrsTSb;X4-EF=Hv51tgZJ)zF$o~IDfuoY1(RZHnafollnj(t#8ut_a(8f zUA;5dqsn}@B|ty22WNff`8(H3B)P%HpO5#IMKxKY)5(Jtz`y$W)BAK;KUL z`mMf7NxN%i;rp)k--G)@p653Af%p3P(O2>QNvZy#Q6qtWznZj5@%ePjuHa+7ZoM$P zD9z9HXKA?~f8M@!@1tMem$!i71B=F&ZicVs^Xv6`)C@yD$IARs_PaFN&F{|-@ve{g n`1R$x4ff+h?!mqPAB`Ca5HEW$H2WQDQvaEgq^gJ;I diff --git a/Themes/Basic Light.theme b/Themes/Basic Light.theme deleted file mode 100644 index e0db52a0e3377d2a1d267c526145b6bd37a141c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1132 zcma)5El&eM5Z&V=Bq&4^LPEec!H}%(?SW|p3XPzqnuK6bXR2!{f{=h9h%=ZaVJUDfUTp0zHy z?kAq_unyLw{m&;4js9oTvz7Zf(*%9KRdrYLnX`mx=$x;t*V^lPcATU3|EnH7Z_TAY z{j{EXk#A+u{?4s^^KWM`7&vP!YT*jK#Q)NB>96Of>*sv*{qlY5eAXcS|HCn(Jc0RX R&q$8iJ@_`vGg{bKH^1@rMH2u3 diff --git a/Themes/Default.theme b/Themes/Default.theme deleted file mode 100644 index 86d33839f34a8c72b0ba6a269ba20a868d1ad79c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1132 zcmZWoF-ROi6rGDoLV|BV(}Todaf#O4~ChzN2vTX?1j;Tn-> zA%U|9L9Vf}6ze-kszbO&A(cg{Sm(W&oy+WI;O)$tKmYx=e|C*Y_2l@O8e`sMz?70N zXTW60vteUcb50Xu5@0x>!MzX$+>dXFUxB~JJP9NKhd=KaxPIT5){>*F9n?>f>=gFC zQUm-8W`b)Q$DC@ZRO;aj-tSn*^RSwkcB-xGo!Fz_{t(o2)yn;L?7{fM{6GjqGYxmn zKg<2+a@#nR*7YCRipzRA)0DV8rE9kh?RC2n^4aD~#d+vE&}W$Me0ybMYOP25qV_~Q z-1+uv__L^V9_#w$sdzBny#Fa4qor)c}kXk@9N>ogbd~mU85+%K8K5E|E1-%*0K=PWZd7a&!25>3W1o7TbbO$X)>4JdET${;OZJfFJav6 a|I4RO_9y^-4LLo*yM?{8GZ+~iF@FKMb2|e7 diff --git a/Themes/Moonshine.theme b/Themes/Moonshine.theme deleted file mode 100644 index 6ec58f14df1c3d18a90c2010783e1146f0118fc2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1132 zcmbtUElk5e6ut(~L)0u;stdNWyDOL)4Co1JsxdPdYS0UAB_SaJK@c?vD#;L-R-iDL zc`VrReb-*-t^~v``SR|2fA`+qYlTSFW!6_hh`k07DfxZ_2tnQ~ZNr{24TT6BXftw9 z*MkjrO@MFkrAOyIKf6c(veLPT3O?h_f{4i|1pu1)c@EIh*y|zQ9%5j_uKIku# z2SL#BZ|7$}d9;uQG%84Q@2=~)g%0)ilBFi0|k+4LC< z8Z6ViuRlw#BOun~%j?(o|Jn+X)9rGXgb*hNEOPKu0~P|@ET!W;WS$6-7$`Nzz?lod z+^$E~zcN2(xx+{pSk_)M=JYh5P@pMtJNc8uSn=5%Vlcm931F}EEOQEipk*8GZz+wB z5xQset$WX@tS{xqRY+Nu(dvHTWEN zGrA5d8CKIZ4jt#Gui23->u=@z7mwq3VDH99?!$Du2l&?Npa*ytO?rU){ys-|f% serialize(Serializer &out) const; + Result deserialize(Deserializer &in); + protected: - void loadFromFile(const std::filesystem::path &path); + void loadColor(const json_t &j, const std::string &name, ImVec4 &color); + void saveColor(json_t &j, const std::string &name, const ImVec4 &color) const; private: bool m_load_ok = false; diff --git a/include/resource/resource.hpp b/include/resource/resource.hpp index b3ae3832..dcfa6a52 100644 --- a/include/resource/resource.hpp +++ b/include/resource/resource.hpp @@ -55,6 +55,9 @@ namespace Toolbox { static UUID64 getResourcePathUUID(const fs_path &path); static UUID64 getResourcePathUUID(fs_path &&path); + const std::vector &getResourcePaths() const & { return m_resource_paths; } + std::optional getResourcePath(const UUID64 &path_uuid) const; + void includeResourcePath(const fs_path &path, bool preload_files); void includeResourcePath(fs_path &&path, bool preload_files); @@ -75,18 +78,25 @@ namespace Toolbox { [[nodiscard]] Result, FSError> getImageHandle(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] Result - getSerialData(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; - [[nodiscard]] Result - getSerialData(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] Result + getSerialData(std::ifstream &in, const fs_path &path, + const UUID64 &resource_path_uuid = 0) const; + [[nodiscard]] Result + getSerialData(std::ifstream &in, fs_path &&path, + const UUID64 &resource_path_uuid = 0) const; [[nodiscard]] Result, FSError> getRawData(const fs_path &path, const UUID64 &resource_path_uuid = 0) const; [[nodiscard]] Result, FSError> getRawData(fs_path &&path, const UUID64 &resource_path_uuid = 0) const; + using PathIterator = Filesystem::directory_iterator; + using RecursivePathIterator = Filesystem::recursive_directory_iterator; + + PathIterator walkIterator(UUID64 resource_path_uuid) const; + RecursivePathIterator walkIteratorRecursive(UUID64 resource_path_uuid) const; + protected: - std::optional getResourcePath(const UUID64 &path_uuid) const; std::optional findResourcePath(const fs_path &sub_path) const; void preloadData(const fs_path &resource_path) const; diff --git a/src/gui/settings/window.cpp b/src/gui/settings/window.cpp index a66a71aa..2bced47b 100644 --- a/src/gui/settings/window.cpp +++ b/src/gui/settings/window.cpp @@ -257,26 +257,30 @@ namespace Toolbox::UI { } void SettingsWindow::renderSettingsUI(TimeStep delta_time) { - ThemeManager &manager = GUIApplication::instance().getThemeManager(); + FontManager &font_manager = GUIApplication::instance().getFontManager(); + ThemeManager &themes_manager = GUIApplication::instance().getThemeManager(); + SettingsManager &settings_manager = GUIApplication::instance().getSettingsManager(); + AppSettings &settings = settings_manager.getCurrentProfile(); - auto themes = manager.themes(); + auto themes = themes_manager.themes(); - size_t selected_index = manager.getActiveThemeIndex(); - RefPtr selected_theme = themes.at(manager.getActiveThemeIndex()); + size_t selected_index = themes_manager.getActiveThemeIndex(); + std::string_view selected_theme_name = themes.size() > 0 ? themes[selected_index]->name() + : ""; - if (ImGui::BeginCombo("Theme", selected_theme->name().data())) { + if (ImGui::BeginCombo("Theme", selected_theme_name.data())) { for (size_t i = 0; i < themes.size(); ++i) { auto theme = themes.at(i); bool selected = i == selected_index; if (ImGui::Selectable(theme->name().data(), selected, ImGuiSelectableFlags_AllowDoubleClick)) { selected_index = i; - manager.applyTheme(theme->name()); + themes_manager.applyTheme(theme->name()); + settings.m_gui_theme = theme->name(); } } ImGui::EndCombo(); } - FontManager &font_manager = GUIApplication::instance().getFontManager(); float current_font_size = font_manager.getCurrentFontSize(); std::string current_font_family = font_manager.getCurrentFontFamily(); @@ -286,6 +290,7 @@ namespace Toolbox::UI { bool selected = family == current_font_family; if (ImGui::Selectable(std::format("{}", family).c_str(), selected)) { font_manager.setCurrentFontFamily(family); + settings.m_font_family = family; } } ImGui::EndCombo(); @@ -296,6 +301,7 @@ namespace Toolbox::UI { bool selected = size == current_font_size; if (ImGui::Selectable(std::format("{}", size).c_str(), selected)) { font_manager.setCurrentFontSize(size); + settings.m_font_size = size; } } ImGui::EndCombo(); diff --git a/src/gui/themes.cpp b/src/gui/themes.cpp index 48e08f52..5ed66710 100644 --- a/src/gui/themes.cpp +++ b/src/gui/themes.cpp @@ -2,22 +2,18 @@ #include "fsystem.hpp" #include "gui/application.hpp" +#include "gui/logging/errors.hpp" #include "gui/themes.hpp" +#include "jsonlib.hpp" namespace Toolbox::UI { - ConfigTheme::ConfigTheme(std::string_view name) : m_name(name) { - auto cwd_result = Toolbox::Filesystem::current_path(); - if (!cwd_result) { - return; - } - loadFromFile(cwd_result.value() / "Themes" / std::format("{}.theme", name)); - } - ConfigTheme::ConfigTheme(std::string_view name, const ImGuiStyle &theme) : m_name(name), m_style(theme), m_load_ok(true) {} + ConfigTheme::ConfigTheme(std::string_view name) : m_name(name) {} + bool ConfigTheme::apply() { - if (!m_load_ok || true) + if (!m_load_ok) return false; auto &style = ImGui::GetStyle(); for (size_t i = 0; i < ImGuiCol_COUNT; ++i) { @@ -26,18 +22,188 @@ namespace Toolbox::UI { return true; } - void ConfigTheme::loadFromFile(const std::filesystem::path &path) { - std::ifstream in(path, std::ios::binary | std::ios::in); +#define THEME_SAVE_COLOR(style, theme_color, j) \ + saveColor(j, TOOLBOX_STRINGIFY_MACRO(theme_color), style.Colors[theme_color]) + + Result ConfigTheme::serialize(Serializer &out) const { + json_t theme_json; + + auto result = tryJSON(theme_json, [this, &out](json_t &j) { + THEME_SAVE_COLOR(m_style, ImGuiCol_Text, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TextDisabled, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_WindowBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ChildBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_PopupBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_Border, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_BorderShadow, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_FrameBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_FrameBgHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_FrameBgActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TitleBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TitleBgActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TitleBgCollapsed, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_MenuBarBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ScrollbarBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ScrollbarGrab, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ScrollbarGrabHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ScrollbarGrabActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_CheckMark, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_SliderGrab, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_SliderGrabActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_Button, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ButtonHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ButtonActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_Header, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_HeaderHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_HeaderActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_Separator, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_SeparatorHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_SeparatorActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ResizeGrip, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ResizeGripHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ResizeGripActive, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TabHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_Tab, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TabSelected, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TabSelectedOverline, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TabDimmed, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TabDimmedSelected, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TabDimmedSelectedOverline, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_DockingPreview, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_DockingEmptyBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_PlotLines, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_PlotLinesHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_PlotHistogram, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_PlotHistogramHovered, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TableHeaderBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TableBorderStrong, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TableBorderLight, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TableRowBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TableRowBgAlt, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TextLink, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_TextSelectedBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_DragDropTarget, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_NavHighlight, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_NavWindowingHighlight, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_NavWindowingDimBg, j); + THEME_SAVE_COLOR(m_style, ImGuiCol_ModalWindowDimBg, j); + + out.stream() << j; + }); + + if (!result) { + JSONError &err = result.error(); + return make_serial_error(err.m_message[0], err.m_reason, err.m_byte, + out.filepath()); + } + + return Result(); + } + +#undef THEME_SAVE_COLOR - in.read(reinterpret_cast(&m_style), sizeof(m_style)); +#define THEME_LOAD_COLOR(style, theme_color, j) \ + loadColor(j, TOOLBOX_STRINGIFY_MACRO(theme_color), style.Colors[theme_color]) + + Result ConfigTheme::deserialize(Deserializer &in) { + json_t theme_json; + + auto result = tryJSON(theme_json, [this, &in](json_t &j) { + in.stream() >> j; + + THEME_LOAD_COLOR(m_style, ImGuiCol_Text, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TextDisabled, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_WindowBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ChildBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_PopupBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_Border, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_BorderShadow, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_FrameBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_FrameBgHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_FrameBgActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TitleBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TitleBgActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TitleBgCollapsed, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_MenuBarBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ScrollbarBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ScrollbarGrab, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ScrollbarGrabHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ScrollbarGrabActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_CheckMark, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_SliderGrab, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_SliderGrabActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_Button, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ButtonHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ButtonActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_Header, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_HeaderHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_HeaderActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_Separator, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_SeparatorHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_SeparatorActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ResizeGrip, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ResizeGripHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ResizeGripActive, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TabHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_Tab, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TabSelected, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TabSelectedOverline, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TabDimmed, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TabDimmedSelected, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TabDimmedSelectedOverline, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_DockingPreview, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_DockingEmptyBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_PlotLines, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_PlotLinesHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_PlotHistogram, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_PlotHistogramHovered, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TableHeaderBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TableBorderStrong, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TableBorderLight, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TableRowBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TableRowBgAlt, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TextLink, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_TextSelectedBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_DragDropTarget, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_NavHighlight, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_NavWindowingHighlight, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_NavWindowingDimBg, j); + THEME_LOAD_COLOR(m_style, ImGuiCol_ModalWindowDimBg, j); + }); + + if (!result) { + JSONError &err = result.error(); + return make_serial_error(err.m_message[0], err.m_reason, err.m_byte, + in.filepath()); + } m_load_ok = true; + return Result(); } - void ConfigTheme::saveToFile(const std::filesystem::path &path) { - std::ofstream out(path, std::ios::binary | std::ios::out | std::ios::ate); +#undef THEME_LOAD_COLOR + + void ConfigTheme::loadColor(const json_t &j, const std::string &name, ImVec4 &color) { + const json_t &color_entry = j[name]; + if (color_entry.is_array() && color_entry.size() == 4) { + if (color_entry[0].is_number() && color_entry[1].is_number() && + color_entry[2].is_number() && color_entry[3].is_number()) { + color.x = color_entry[0]; + color.y = color_entry[1]; + color.z = color_entry[2]; + color.w = color_entry[3]; + } else { + TOOLBOX_ERROR_V("[THEME] JSON entry for theme {} has corrupted entry {}", m_name, + name); + } + } else { + TOOLBOX_ERROR_V("[THEME] JSON entry for theme {} has corrupted entry {}", m_name, name); + } + } - out.write(reinterpret_cast(&m_style), sizeof(m_style)); + void ConfigTheme::saveColor(json_t &j, const std::string &name, const ImVec4 &color) const { + std::array color_rgba = {color.x, color.y, color.z, color.w}; + j[name] = color_rgba; } Result ThemeManager::initialize() { @@ -47,14 +213,28 @@ namespace Toolbox::UI { return make_fs_error(std::error_code(), {"Themes not found"}); } - for (auto &subpath : Toolbox::Filesystem::directory_iterator{"Themes"}) { + for (auto &subpath : manager.walkIterator(themes_uuid)) { try { - auto theme = make_referable(subpath.path().stem().string()); - if (theme->name() == "Default") { - theme->apply(); - m_active_theme = m_themes.size(); - } - m_themes.emplace_back(theme); + std::ifstream in_str; + manager.getSerialData(in_str, subpath, themes_uuid).and_then([&]() { + Deserializer in(in_str.rdbuf()); + + RefPtr theme = + make_referable(subpath.path().stem().string()); + + theme->deserialize(in).or_else([](const SerialError &error) { + LogError(error); + return Result(); + }); + + if (theme->name() == "Default") { + theme->apply(); + m_active_theme = m_themes.size(); + } + m_themes.emplace_back(theme); + + return Result(); + }); } catch (std::runtime_error &e) { return make_fs_error(std::error_code(), {e.what()}); } diff --git a/src/gui/window.cpp b/src/gui/window.cpp index adb1df16..64375170 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -29,7 +29,7 @@ namespace Toolbox::UI { void ImWindow::setIcon(const std::string &icon_name) { ResourceManager &res_manager = GUIApplication::instance().getResourceManager(); - UUID64 icon_dir = res_manager.getResourcePathUUID(fs_path("Images") / "Icons"); + UUID64 icon_dir = res_manager.getResourcePathUUID("Images/Icons"); auto result = res_manager.getRawData(icon_name, icon_dir); if (!result) { diff --git a/src/resource/resource.cpp b/src/resource/resource.cpp index fe698d6c..e670b5be 100644 --- a/src/resource/resource.cpp +++ b/src/resource/resource.cpp @@ -146,6 +146,11 @@ namespace Toolbox { return Result, FSError>(m_image_handle_cache[path]); } + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), + {"Resource path not found"}); + } + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { const ResourceData &data = m_data_preload_cache[path]; @@ -161,11 +166,6 @@ namespace Toolbox { return handle; } - if (!hasDataPath(path, resource_path_uuid)) { - return make_fs_error>(std::error_code(), - {"Resource path not found"}); - } - fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; @@ -184,6 +184,11 @@ namespace Toolbox { return Result, FSError>(m_image_handle_cache[path]); } + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), + {"Resource path not found"}); + } + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { const ResourceData &data = m_data_preload_cache[path]; @@ -199,11 +204,6 @@ namespace Toolbox { return handle; } - if (!hasDataPath(path, resource_path_uuid)) { - return make_fs_error>(std::error_code(), - {"Resource path not found"}); - } - fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; @@ -216,51 +216,51 @@ namespace Toolbox { return handle; } - Result - ResourceManager::getSerialData(const fs_path &path, const UUID64 &resource_path_uuid) const { + Result ResourceManager::getSerialData(std::ifstream &in, const fs_path &path, + const UUID64 &resource_path_uuid) const { if (!hasDataPath(path, resource_path_uuid)) { - return make_fs_error(std::error_code(), {"Resource path not found"}); + return make_fs_error(std::error_code(), {"Resource path not found"}); } fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; - std::ifstream in(abs_path, std::ios::in | std::ios::binary); + in.open(abs_path); if (!in.is_open()) { - return make_fs_error(std::error_code(), {"Failed to open file"}); + return make_fs_error(std::error_code(), {"Failed to open file"}); } - return in; + return {}; } - Result - ResourceManager::getSerialData(fs_path &&path, const UUID64 &resource_path_uuid) const { + Result ResourceManager::getSerialData(std::ifstream &in, fs_path &&path, + const UUID64 &resource_path_uuid) const { if (!hasDataPath(path, resource_path_uuid)) { - return make_fs_error(std::error_code(), {"Resource path not found"}); + return make_fs_error(std::error_code(), {"Resource path not found"}); } fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; - std::ifstream in(abs_path, std::ios::in | std::ios::binary); + in.open(abs_path); if (!in.is_open()) { - return make_fs_error(std::error_code(), {"Failed to open file"}); + return make_fs_error(std::error_code(), {"Failed to open file"}); } - return in; + return {}; } Result, FSError> ResourceManager::getRawData(const fs_path &path, const UUID64 &resource_path_uuid) const { + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), {"Resource path not found"}); + } + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { const ResourceData &data = m_data_preload_cache[path]; return std::span(static_cast(data.m_data_ptr), data.m_data_size); } - if (!hasDataPath(path, resource_path_uuid)) { - return make_fs_error>(std::error_code(), {"Resource path not found"}); - } - fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; @@ -295,15 +295,15 @@ namespace Toolbox { Result, FSError> ResourceManager::getRawData(fs_path &&path, const UUID64 &resource_path_uuid) const { + if (!hasDataPath(path, resource_path_uuid)) { + return make_fs_error>(std::error_code(), {"Resource path not found"}); + } + if (m_data_preload_cache.find(path) != m_data_preload_cache.end()) { const ResourceData &data = m_data_preload_cache[path]; return std::span(static_cast(data.m_data_ptr), data.m_data_size); } - if (!hasDataPath(path, resource_path_uuid)) { - return make_fs_error>(std::error_code(), {"Resource path not found"}); - } - fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); fs_path abs_path = resource_path / path; @@ -336,6 +336,17 @@ namespace Toolbox { return std::span(static_cast(buffer), file_size); } + ResourceManager::PathIterator ResourceManager::walkIterator(UUID64 resource_path_uuid) const { + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + return PathIterator(resource_path); + } + + ResourceManager::RecursivePathIterator + ResourceManager::walkIteratorRecursive(UUID64 resource_path_uuid) const { + fs_path resource_path = getResourcePath(resource_path_uuid).value_or(fs_path()); + return RecursivePathIterator(resource_path); + } + std::optional ResourceManager::getResourcePath(const UUID64 &path_uuid) const { for (const ResourcePath &resource_path : m_resource_paths) { if (resource_path.getUUID() == path_uuid) { @@ -375,14 +386,17 @@ namespace Toolbox { } UUID64 ResourceManager::getResourcePathUUID(const fs_path &path) { + fs_path norm_path = path; + norm_path.make_preferred(); if (!path.is_absolute()) { - fs_path abs_path = Filesystem::weakly_canonical(path).value_or(path); - return UUID64(std::hash{}(abs_path)); + norm_path = Filesystem::weakly_canonical(norm_path).value_or(norm_path); + return UUID64(std::hash{}(norm_path)); } - return UUID64(std::hash{}(path)); + return UUID64(std::hash{}(norm_path)); } UUID64 ResourceManager::getResourcePathUUID(fs_path &&path) { + path.make_preferred(); if (!path.is_absolute()) { path = Filesystem::weakly_canonical(path).value_or(path); } From 2328eba09f2225eebf60372d90f1ed0d766b4e4f Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Thu, 26 Sep 2024 15:49:19 -0500 Subject: [PATCH 8/9] Prevent memory leak --- src/resource/resource.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/resource/resource.cpp b/src/resource/resource.cpp index e670b5be..aab013fd 100644 --- a/src/resource/resource.cpp +++ b/src/resource/resource.cpp @@ -4,7 +4,16 @@ namespace Toolbox { - ResourceManager::~ResourceManager() {} + ResourceManager::~ResourceManager() { + m_resource_paths.clear(); + m_image_handle_cache.clear(); + + for (auto& [_, data] : m_data_preload_cache) { + std::free(data.m_data_ptr); + } + + m_data_preload_cache.clear(); + } void ResourceManager::includeResourcePath(const fs_path &path, bool preload_files) { if (hasResourcePath(path)) { From e9d594cbcd9bedd4c7651d39acb28cca0636893c Mon Sep 17 00:00:00 2001 From: JoshuaMKW Date: Thu, 26 Sep 2024 19:14:06 -0500 Subject: [PATCH 9/9] Supply basic themes to try and fix GitHub CI --- Themes/BasicDark.json | 350 +++++++++++++++++++++++++++++++++++++++++ Themes/BasicLight.json | 350 +++++++++++++++++++++++++++++++++++++++++ Themes/Default.json | 350 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1050 insertions(+) create mode 100644 Themes/BasicDark.json create mode 100644 Themes/BasicLight.json create mode 100644 Themes/Default.json diff --git a/Themes/BasicDark.json b/Themes/BasicDark.json new file mode 100644 index 00000000..a3c21367 --- /dev/null +++ b/Themes/BasicDark.json @@ -0,0 +1,350 @@ +{ + "ImGuiCol_Border": [ + 0.4300000071525574, + 0.4300000071525574, + 0.5, + 0.5 + ], + "ImGuiCol_BorderShadow": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_Button": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.4000000059604645 + ], + "ImGuiCol_ButtonActive": [ + 0.05999999865889549, + 0.5299999713897705, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_ButtonHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_CheckMark": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_ChildBg": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_DockingEmptyBg": [ + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224, + 1.0 + ], + "ImGuiCol_DockingPreview": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.699999988079071 + ], + "ImGuiCol_DragDropTarget": [ + 1.0, + 1.0, + 0.0, + 0.8999999761581421 + ], + "ImGuiCol_FrameBg": [ + 0.1599999964237213, + 0.28999999165534973, + 0.47999998927116394, + 0.5400000214576721 + ], + "ImGuiCol_FrameBgActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.6700000166893005 + ], + "ImGuiCol_FrameBgHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.4000000059604645 + ], + "ImGuiCol_Header": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.3100000023841858 + ], + "ImGuiCol_HeaderActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_HeaderHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_MenuBarBg": [ + 0.14000000059604645, + 0.14000000059604645, + 0.14000000059604645, + 1.0 + ], + "ImGuiCol_ModalWindowDimBg": [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 0.3499999940395355 + ], + "ImGuiCol_NavHighlight": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_NavWindowingDimBg": [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 0.20000000298023224 + ], + "ImGuiCol_NavWindowingHighlight": [ + 1.0, + 1.0, + 1.0, + 0.699999988079071 + ], + "ImGuiCol_PlotHistogram": [ + 0.8999999761581421, + 0.699999988079071, + 0.0, + 1.0 + ], + "ImGuiCol_PlotHistogramHovered": [ + 1.0, + 0.6000000238418579, + 0.0, + 1.0 + ], + "ImGuiCol_PlotLines": [ + 0.6100000143051147, + 0.6100000143051147, + 0.6100000143051147, + 1.0 + ], + "ImGuiCol_PlotLinesHovered": [ + 1.0, + 0.4300000071525574, + 0.3499999940395355, + 1.0 + ], + "ImGuiCol_PopupBg": [ + 0.07999999821186066, + 0.07999999821186066, + 0.07999999821186066, + 0.9399999976158142 + ], + "ImGuiCol_ResizeGrip": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.20000000298023224 + ], + "ImGuiCol_ResizeGripActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.949999988079071 + ], + "ImGuiCol_ResizeGripHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.6700000166893005 + ], + "ImGuiCol_ScrollbarBg": [ + 0.019999999552965164, + 0.019999999552965164, + 0.019999999552965164, + 0.5299999713897705 + ], + "ImGuiCol_ScrollbarGrab": [ + 0.3100000023841858, + 0.3100000023841858, + 0.3100000023841858, + 1.0 + ], + "ImGuiCol_ScrollbarGrabActive": [ + 0.5099999904632568, + 0.5099999904632568, + 0.5099999904632568, + 1.0 + ], + "ImGuiCol_ScrollbarGrabHovered": [ + 0.4099999964237213, + 0.4099999964237213, + 0.4099999964237213, + 1.0 + ], + "ImGuiCol_Separator": [ + 0.4300000071525574, + 0.4300000071525574, + 0.5, + 0.5 + ], + "ImGuiCol_SeparatorActive": [ + 0.10000000149011612, + 0.4000000059604645, + 0.75, + 1.0 + ], + "ImGuiCol_SeparatorHovered": [ + 0.10000000149011612, + 0.4000000059604645, + 0.75, + 0.7799999713897705 + ], + "ImGuiCol_SliderGrab": [ + 0.23999999463558197, + 0.5199999809265137, + 0.8799999952316284, + 1.0 + ], + "ImGuiCol_SliderGrabActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_Tab": [ + 0.17999999225139618, + 0.34999996423721313, + 0.5800000429153442, + 0.8619999885559082 + ], + "ImGuiCol_TabDimmed": [ + 0.06800000369548798, + 0.10199998319149017, + 0.14800003170967102, + 0.9724000096321106 + ], + "ImGuiCol_TabDimmedSelected": [ + 0.13599997758865356, + 0.2619999647140503, + 0.42399999499320984, + 1.0 + ], + "ImGuiCol_TabDimmedSelectedOverline": [ + 0.5, + 0.5, + 0.5, + 1.0 + ], + "ImGuiCol_TabHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_TabSelected": [ + 0.19999998807907104, + 0.4099999666213989, + 0.6800000071525574, + 1.0 + ], + "ImGuiCol_TabSelectedOverline": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TableBorderLight": [ + 0.23000000417232513, + 0.23000000417232513, + 0.25, + 1.0 + ], + "ImGuiCol_TableBorderStrong": [ + 0.3100000023841858, + 0.3100000023841858, + 0.3499999940395355, + 1.0 + ], + "ImGuiCol_TableHeaderBg": [ + 0.1899999976158142, + 0.1899999976158142, + 0.20000000298023224, + 1.0 + ], + "ImGuiCol_TableRowBg": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_TableRowBgAlt": [ + 1.0, + 1.0, + 1.0, + 0.05999999865889549 + ], + "ImGuiCol_Text": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "ImGuiCol_TextDisabled": [ + 0.5, + 0.5, + 0.5, + 1.0 + ], + "ImGuiCol_TextLink": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TextSelectedBg": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.3499999940395355 + ], + "ImGuiCol_TitleBg": [ + 0.03999999910593033, + 0.03999999910593033, + 0.03999999910593033, + 1.0 + ], + "ImGuiCol_TitleBgActive": [ + 0.1599999964237213, + 0.28999999165534973, + 0.47999998927116394, + 1.0 + ], + "ImGuiCol_TitleBgCollapsed": [ + 0.0, + 0.0, + 0.0, + 0.5099999904632568 + ], + "ImGuiCol_WindowBg": [ + 0.05999999865889549, + 0.05999999865889549, + 0.05999999865889549, + 0.9399999976158142 + ] +} \ No newline at end of file diff --git a/Themes/BasicLight.json b/Themes/BasicLight.json new file mode 100644 index 00000000..c72e4353 --- /dev/null +++ b/Themes/BasicLight.json @@ -0,0 +1,350 @@ +{ + "ImGuiCol_Border": [ + 0.0, + 0.0, + 0.0, + 0.30000001192092896 + ], + "ImGuiCol_BorderShadow": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_Button": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.4000000059604645 + ], + "ImGuiCol_ButtonActive": [ + 0.05999999865889549, + 0.5299999713897705, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_ButtonHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_CheckMark": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_ChildBg": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_DockingEmptyBg": [ + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224, + 1.0 + ], + "ImGuiCol_DockingPreview": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.21699999272823334 + ], + "ImGuiCol_DragDropTarget": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.949999988079071 + ], + "ImGuiCol_FrameBg": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "ImGuiCol_FrameBgActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.6700000166893005 + ], + "ImGuiCol_FrameBgHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.4000000059604645 + ], + "ImGuiCol_Header": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.3100000023841858 + ], + "ImGuiCol_HeaderActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_HeaderHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_MenuBarBg": [ + 0.8600000143051147, + 0.8600000143051147, + 0.8600000143051147, + 1.0 + ], + "ImGuiCol_ModalWindowDimBg": [ + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224, + 0.3499999940395355 + ], + "ImGuiCol_NavHighlight": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_NavWindowingDimBg": [ + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224 + ], + "ImGuiCol_NavWindowingHighlight": [ + 0.699999988079071, + 0.699999988079071, + 0.699999988079071, + 0.699999988079071 + ], + "ImGuiCol_PlotHistogram": [ + 0.8999999761581421, + 0.699999988079071, + 0.0, + 1.0 + ], + "ImGuiCol_PlotHistogramHovered": [ + 1.0, + 0.44999998807907104, + 0.0, + 1.0 + ], + "ImGuiCol_PlotLines": [ + 0.38999998569488525, + 0.38999998569488525, + 0.38999998569488525, + 1.0 + ], + "ImGuiCol_PlotLinesHovered": [ + 1.0, + 0.4300000071525574, + 0.3499999940395355, + 1.0 + ], + "ImGuiCol_PopupBg": [ + 1.0, + 1.0, + 1.0, + 0.9800000190734863 + ], + "ImGuiCol_ResizeGrip": [ + 0.3499999940395355, + 0.3499999940395355, + 0.3499999940395355, + 0.17000000178813934 + ], + "ImGuiCol_ResizeGripActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.949999988079071 + ], + "ImGuiCol_ResizeGripHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.6700000166893005 + ], + "ImGuiCol_ScrollbarBg": [ + 0.9800000190734863, + 0.9800000190734863, + 0.9800000190734863, + 0.5299999713897705 + ], + "ImGuiCol_ScrollbarGrab": [ + 0.6899999976158142, + 0.6899999976158142, + 0.6899999976158142, + 0.800000011920929 + ], + "ImGuiCol_ScrollbarGrabActive": [ + 0.49000000953674316, + 0.49000000953674316, + 0.49000000953674316, + 1.0 + ], + "ImGuiCol_ScrollbarGrabHovered": [ + 0.49000000953674316, + 0.49000000953674316, + 0.49000000953674316, + 0.800000011920929 + ], + "ImGuiCol_Separator": [ + 0.38999998569488525, + 0.38999998569488525, + 0.38999998569488525, + 0.6200000047683716 + ], + "ImGuiCol_SeparatorActive": [ + 0.14000000059604645, + 0.4399999976158142, + 0.800000011920929, + 1.0 + ], + "ImGuiCol_SeparatorHovered": [ + 0.14000000059604645, + 0.4399999976158142, + 0.800000011920929, + 0.7799999713897705 + ], + "ImGuiCol_SliderGrab": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.7799999713897705 + ], + "ImGuiCol_SliderGrabActive": [ + 0.46000000834465027, + 0.5400000214576721, + 0.800000011920929, + 0.6000000238418579 + ], + "ImGuiCol_Tab": [ + 0.7639999985694885, + 0.796999990940094, + 0.8359999656677246, + 0.9309999942779541 + ], + "ImGuiCol_TabDimmed": [ + 0.920799970626831, + 0.9273999929428101, + 0.9351999759674072, + 0.9861999750137329 + ], + "ImGuiCol_TabDimmedSelected": [ + 0.7416000366210938, + 0.8208000063896179, + 0.9143999814987183, + 1.0 + ], + "ImGuiCol_TabDimmedSelectedOverline": [ + 0.25999999046325684, + 0.5899999737739563, + 1.0, + 1.0 + ], + "ImGuiCol_TabHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_TabSelected": [ + 0.5960000157356262, + 0.7279999852180481, + 0.8840000033378601, + 1.0 + ], + "ImGuiCol_TabSelectedOverline": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TableBorderLight": [ + 0.6800000071525574, + 0.6800000071525574, + 0.7400000095367432, + 1.0 + ], + "ImGuiCol_TableBorderStrong": [ + 0.5699999928474426, + 0.5699999928474426, + 0.6399999856948853, + 1.0 + ], + "ImGuiCol_TableHeaderBg": [ + 0.7799999713897705, + 0.8700000047683716, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TableRowBg": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_TableRowBgAlt": [ + 0.30000001192092896, + 0.30000001192092896, + 0.30000001192092896, + 0.09000000357627869 + ], + "ImGuiCol_Text": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "ImGuiCol_TextDisabled": [ + 0.6000000238418579, + 0.6000000238418579, + 0.6000000238418579, + 1.0 + ], + "ImGuiCol_TextLink": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TextSelectedBg": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.3499999940395355 + ], + "ImGuiCol_TitleBg": [ + 0.9599999785423279, + 0.9599999785423279, + 0.9599999785423279, + 1.0 + ], + "ImGuiCol_TitleBgActive": [ + 0.8199999928474426, + 0.8199999928474426, + 0.8199999928474426, + 1.0 + ], + "ImGuiCol_TitleBgCollapsed": [ + 1.0, + 1.0, + 1.0, + 0.5099999904632568 + ], + "ImGuiCol_WindowBg": [ + 0.9399999976158142, + 0.9399999976158142, + 0.9399999976158142, + 1.0 + ] +} \ No newline at end of file diff --git a/Themes/Default.json b/Themes/Default.json new file mode 100644 index 00000000..a3c21367 --- /dev/null +++ b/Themes/Default.json @@ -0,0 +1,350 @@ +{ + "ImGuiCol_Border": [ + 0.4300000071525574, + 0.4300000071525574, + 0.5, + 0.5 + ], + "ImGuiCol_BorderShadow": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_Button": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.4000000059604645 + ], + "ImGuiCol_ButtonActive": [ + 0.05999999865889549, + 0.5299999713897705, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_ButtonHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_CheckMark": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_ChildBg": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_DockingEmptyBg": [ + 0.20000000298023224, + 0.20000000298023224, + 0.20000000298023224, + 1.0 + ], + "ImGuiCol_DockingPreview": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.699999988079071 + ], + "ImGuiCol_DragDropTarget": [ + 1.0, + 1.0, + 0.0, + 0.8999999761581421 + ], + "ImGuiCol_FrameBg": [ + 0.1599999964237213, + 0.28999999165534973, + 0.47999998927116394, + 0.5400000214576721 + ], + "ImGuiCol_FrameBgActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.6700000166893005 + ], + "ImGuiCol_FrameBgHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.4000000059604645 + ], + "ImGuiCol_Header": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.3100000023841858 + ], + "ImGuiCol_HeaderActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_HeaderHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_MenuBarBg": [ + 0.14000000059604645, + 0.14000000059604645, + 0.14000000059604645, + 1.0 + ], + "ImGuiCol_ModalWindowDimBg": [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 0.3499999940395355 + ], + "ImGuiCol_NavHighlight": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_NavWindowingDimBg": [ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 0.20000000298023224 + ], + "ImGuiCol_NavWindowingHighlight": [ + 1.0, + 1.0, + 1.0, + 0.699999988079071 + ], + "ImGuiCol_PlotHistogram": [ + 0.8999999761581421, + 0.699999988079071, + 0.0, + 1.0 + ], + "ImGuiCol_PlotHistogramHovered": [ + 1.0, + 0.6000000238418579, + 0.0, + 1.0 + ], + "ImGuiCol_PlotLines": [ + 0.6100000143051147, + 0.6100000143051147, + 0.6100000143051147, + 1.0 + ], + "ImGuiCol_PlotLinesHovered": [ + 1.0, + 0.4300000071525574, + 0.3499999940395355, + 1.0 + ], + "ImGuiCol_PopupBg": [ + 0.07999999821186066, + 0.07999999821186066, + 0.07999999821186066, + 0.9399999976158142 + ], + "ImGuiCol_ResizeGrip": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.20000000298023224 + ], + "ImGuiCol_ResizeGripActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.949999988079071 + ], + "ImGuiCol_ResizeGripHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.6700000166893005 + ], + "ImGuiCol_ScrollbarBg": [ + 0.019999999552965164, + 0.019999999552965164, + 0.019999999552965164, + 0.5299999713897705 + ], + "ImGuiCol_ScrollbarGrab": [ + 0.3100000023841858, + 0.3100000023841858, + 0.3100000023841858, + 1.0 + ], + "ImGuiCol_ScrollbarGrabActive": [ + 0.5099999904632568, + 0.5099999904632568, + 0.5099999904632568, + 1.0 + ], + "ImGuiCol_ScrollbarGrabHovered": [ + 0.4099999964237213, + 0.4099999964237213, + 0.4099999964237213, + 1.0 + ], + "ImGuiCol_Separator": [ + 0.4300000071525574, + 0.4300000071525574, + 0.5, + 0.5 + ], + "ImGuiCol_SeparatorActive": [ + 0.10000000149011612, + 0.4000000059604645, + 0.75, + 1.0 + ], + "ImGuiCol_SeparatorHovered": [ + 0.10000000149011612, + 0.4000000059604645, + 0.75, + 0.7799999713897705 + ], + "ImGuiCol_SliderGrab": [ + 0.23999999463558197, + 0.5199999809265137, + 0.8799999952316284, + 1.0 + ], + "ImGuiCol_SliderGrabActive": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_Tab": [ + 0.17999999225139618, + 0.34999996423721313, + 0.5800000429153442, + 0.8619999885559082 + ], + "ImGuiCol_TabDimmed": [ + 0.06800000369548798, + 0.10199998319149017, + 0.14800003170967102, + 0.9724000096321106 + ], + "ImGuiCol_TabDimmedSelected": [ + 0.13599997758865356, + 0.2619999647140503, + 0.42399999499320984, + 1.0 + ], + "ImGuiCol_TabDimmedSelectedOverline": [ + 0.5, + 0.5, + 0.5, + 1.0 + ], + "ImGuiCol_TabHovered": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.800000011920929 + ], + "ImGuiCol_TabSelected": [ + 0.19999998807907104, + 0.4099999666213989, + 0.6800000071525574, + 1.0 + ], + "ImGuiCol_TabSelectedOverline": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TableBorderLight": [ + 0.23000000417232513, + 0.23000000417232513, + 0.25, + 1.0 + ], + "ImGuiCol_TableBorderStrong": [ + 0.3100000023841858, + 0.3100000023841858, + 0.3499999940395355, + 1.0 + ], + "ImGuiCol_TableHeaderBg": [ + 0.1899999976158142, + 0.1899999976158142, + 0.20000000298023224, + 1.0 + ], + "ImGuiCol_TableRowBg": [ + 0.0, + 0.0, + 0.0, + 0.0 + ], + "ImGuiCol_TableRowBgAlt": [ + 1.0, + 1.0, + 1.0, + 0.05999999865889549 + ], + "ImGuiCol_Text": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "ImGuiCol_TextDisabled": [ + 0.5, + 0.5, + 0.5, + 1.0 + ], + "ImGuiCol_TextLink": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 1.0 + ], + "ImGuiCol_TextSelectedBg": [ + 0.25999999046325684, + 0.5899999737739563, + 0.9800000190734863, + 0.3499999940395355 + ], + "ImGuiCol_TitleBg": [ + 0.03999999910593033, + 0.03999999910593033, + 0.03999999910593033, + 1.0 + ], + "ImGuiCol_TitleBgActive": [ + 0.1599999964237213, + 0.28999999165534973, + 0.47999998927116394, + 1.0 + ], + "ImGuiCol_TitleBgCollapsed": [ + 0.0, + 0.0, + 0.0, + 0.5099999904632568 + ], + "ImGuiCol_WindowBg": [ + 0.05999999865889549, + 0.05999999865889549, + 0.05999999865889549, + 0.9399999976158142 + ] +} \ No newline at end of file