diff --git a/assets/screenshot.png b/assets/screenshot.png index 4233413..2f33bb2 100644 Binary files a/assets/screenshot.png and b/assets/screenshot.png differ diff --git a/src/core/config/config.cpp b/src/core/config/config.cpp index b440e9e..fce655c 100644 --- a/src/core/config/config.cpp +++ b/src/core/config/config.cpp @@ -1,6 +1,6 @@ #include "config.hpp" -#include "core/utils.hpp" #include "core/error.hpp" +#include "core/utils.hpp" #include #include @@ -25,11 +25,12 @@ Result config::initialize(std::unique_ptr file) m_file = std::move(file); if (!m_file) return Err(error_code::config_missing, "Config file is missing"); - + auto parse_result = m_file->parse(); if (!parse_result) - return Err(error_code::config_invalid, parse_result.error().message, parse_result.error().context); - + return Err(error_code::config_invalid, parse_result.error().message, + parse_result.error().context); + return Ok(); } @@ -68,10 +69,10 @@ void config::copy_file(const std::filesystem::path &src, const std::filesystem:: std::ifstream in(src, std::ios::binary); std::ofstream out(dst, std::ios::binary); - + if (!in || !out) return; - + out << in.rdbuf(); } @@ -79,7 +80,7 @@ void config::copy_dir(const std::filesystem::path &src, const std::filesystem::p { if (!std::filesystem::exists(src)) return; - + for (auto const &entry : std::filesystem::recursive_directory_iterator(src)) { auto rel = std::filesystem::relative(entry.path(), src); @@ -166,7 +167,7 @@ Result config::set_default_theme(const std::string &theme) { if (!m_file) return Err(error_code::config_missing, "Configuration not initialized"); - + m_file->set_value("general", "default_theme", theme); return m_file->save_file(); } @@ -175,7 +176,7 @@ Result config::set_palettes_path(const std::string &path) { if (!m_file) return Err(error_code::config_missing, "Configuration not initialized"); - + m_file->set_value("general", "palettes_path", path); return m_file->save_file(); } @@ -184,7 +185,7 @@ Result config::set_font(const std::string &font) { if (!m_file) return Err(error_code::config_missing, "Configuration not initialized"); - + m_file->set_value("general", "font", font); return m_file->save_file(); } @@ -192,17 +193,17 @@ Result config::set_font_size(int font_size) { if (!m_file) return Err(error_code::config_missing, "Configuration not initialized"); - + m_file->set_value("general", "font_size", font_size); return m_file->save_file(); } Result config::update_template(const std::string &key, - const clrsync::core::theme_template &theme_template) + const clrsync::core::theme_template &theme_template) { if (!m_file) return Err(error_code::config_missing, "Configuration not initialized"); - + m_themes[key] = theme_template; m_file->set_value("templates." + key, "input_path", theme_template.template_path()); m_file->set_value("templates." + key, "output_path", theme_template.output_path()); @@ -215,25 +216,29 @@ Result config::remove_template(const std::string &key) { if (!m_file) return Err(error_code::config_missing, "Configuration not initialized"); - + auto it = m_themes.find(key); if (it == m_themes.end()) return Err(error_code::template_not_found, "Template not found", key); - + std::filesystem::path template_file = it->second.template_path(); if (std::filesystem::exists(template_file)) { - try { + try + { std::filesystem::remove(template_file); - } catch (const std::exception& e) { - return Err(error_code::file_write_failed, "Failed to delete template file", e.what()); + } + catch (const std::exception &e) + { + return Err(error_code::file_write_failed, "Failed to delete template file", + e.what()); } } - + m_themes.erase(it); - + m_file->remove_section("templates." + key); - + return m_file->save_file(); } @@ -264,14 +269,16 @@ const std::unordered_map config::tem return m_themes; } -Result config::template_by_name(const std::string &name) const +Result config::template_by_name( + const std::string &name) const { auto it = m_themes.find(name); if (it != m_themes.end()) { return Ok(&it->second); } - return Err(error_code::template_not_found, "Template not found", name); + return Err(error_code::template_not_found, + "Template not found", name); } } // namespace clrsync::core diff --git a/src/core/config/config.hpp b/src/core/config/config.hpp index cab4b55..299d722 100644 --- a/src/core/config/config.hpp +++ b/src/core/config/config.hpp @@ -1,9 +1,9 @@ #ifndef CLRSYNC_CORE_CONFIG_HPP #define CLRSYNC_CORE_CONFIG_HPP +#include #include #include -#include #include #include #include @@ -22,17 +22,16 @@ class config const std::string &palettes_path(); const std::string default_theme() const; const std::unordered_map templates(); - Result template_by_name(const std::string &name) const; + Result template_by_name(const std::string &name) const; std::filesystem::path get_user_config_dir(); - Result set_default_theme(const std::string &theme); Result set_palettes_path(const std::string &path); Result set_font(const std::string &font); Result set_font_size(int font_size); Result update_template(const std::string &key, - const clrsync::core::theme_template &theme_template); + const clrsync::core::theme_template &theme_template); Result remove_template(const std::string &key); static std::filesystem::path get_data_dir(); @@ -44,8 +43,8 @@ class config std::string m_palettes_dir{}; std::unique_ptr m_file; std::unordered_map m_themes{}; - static void copy_file(const std::filesystem::path& src, const std::filesystem::path& dst); - static void copy_dir(const std::filesystem::path& src, const std::filesystem::path& dst); + static void copy_file(const std::filesystem::path &src, const std::filesystem::path &dst); + static void copy_dir(const std::filesystem::path &src, const std::filesystem::path &dst); void copy_default_configs(); }; } // namespace clrsync::core diff --git a/src/core/error.hpp b/src/core/error.hpp index 0833417..fb1bb96 100644 --- a/src/core/error.hpp +++ b/src/core/error.hpp @@ -1,9 +1,9 @@ #ifndef CLRSYNC_CORE_ERROR_HPP #define CLRSYNC_CORE_ERROR_HPP +#include #include #include -#include namespace clrsync::core { @@ -11,54 +11,73 @@ namespace clrsync::core enum class error_code { unknown, - + file_not_found, file_open_failed, file_write_failed, file_read_failed, dir_create_failed, - + parse_failed, invalid_format, - + config_missing, config_invalid, - + template_not_found, template_load_failed, template_apply_failed, - + palette_not_found, palette_load_failed, - + init_failed, invalid_arg, resource_missing, }; -inline const char* error_code_string(error_code code) +inline const char *error_code_string(error_code code) { switch (code) { - case error_code::unknown: return "Unknown error"; - case error_code::file_not_found: return "File not found"; - case error_code::file_open_failed: return "Failed to open file"; - case error_code::file_write_failed: return "Failed to write file"; - case error_code::file_read_failed: return "Failed to read file"; - case error_code::dir_create_failed: return "Failed to create directory"; - case error_code::parse_failed: return "Parse failed"; - case error_code::invalid_format: return "Invalid format"; - case error_code::config_missing: return "Configuration missing"; - case error_code::config_invalid: return "Configuration invalid"; - case error_code::template_not_found: return "Template not found"; - case error_code::template_load_failed: return "Failed to load template"; - case error_code::template_apply_failed: return "Failed to apply template"; - case error_code::palette_not_found: return "Palette not found"; - case error_code::palette_load_failed: return "Failed to load palette"; - case error_code::init_failed: return "Initialization failed"; - case error_code::invalid_arg: return "Invalid argument"; - case error_code::resource_missing: return "Resource missing"; - default: return "Unknown error code"; + case error_code::unknown: + return "Unknown error"; + case error_code::file_not_found: + return "File not found"; + case error_code::file_open_failed: + return "Failed to open file"; + case error_code::file_write_failed: + return "Failed to write file"; + case error_code::file_read_failed: + return "Failed to read file"; + case error_code::dir_create_failed: + return "Failed to create directory"; + case error_code::parse_failed: + return "Parse failed"; + case error_code::invalid_format: + return "Invalid format"; + case error_code::config_missing: + return "Configuration missing"; + case error_code::config_invalid: + return "Configuration invalid"; + case error_code::template_not_found: + return "Template not found"; + case error_code::template_load_failed: + return "Failed to load template"; + case error_code::template_apply_failed: + return "Failed to apply template"; + case error_code::palette_not_found: + return "Palette not found"; + case error_code::palette_load_failed: + return "Failed to load palette"; + case error_code::init_failed: + return "Initialization failed"; + case error_code::invalid_arg: + return "Invalid argument"; + case error_code::resource_missing: + return "Resource missing"; + default: + return "Unknown error code"; } } @@ -67,15 +86,20 @@ struct Error error_code code; std::string message; std::string context; - - Error(error_code c) : code(c), message(error_code_string(c)) {} - - Error(error_code c, std::string msg) - : code(c), message(std::move(msg)) {} - + + Error(error_code c) : code(c), message(error_code_string(c)) + { + } + + Error(error_code c, std::string msg) : code(c), message(std::move(msg)) + { + } + Error(error_code c, std::string msg, std::string ctx) - : code(c), message(std::move(msg)), context(std::move(ctx)) {} - + : code(c), message(std::move(msg)), context(std::move(ctx)) + { + } + std::string description() const { if (context.empty()) @@ -84,59 +108,81 @@ struct Error } }; -template -class [[nodiscard]] Result +template class [[nodiscard]] Result { -private: + private: std::variant m_data; - -public: - Result(T value) : m_data(std::move(value)) {} - - Result(Error error) : m_data(std::move(error)) {} - - bool is_ok() const { return std::holds_alternative(m_data); } - - bool is_error() const { return std::holds_alternative(m_data); } - - explicit operator bool() const { return is_ok(); } - - T& value() & { return std::get(m_data); } - const T& value() const & { return std::get(m_data); } - T&& value() && { return std::get(std::move(m_data)); } - - const Error& error() const { return std::get(m_data); } - + + public: + Result(T value) : m_data(std::move(value)) + { + } + + Result(Error error) : m_data(std::move(error)) + { + } + + bool is_ok() const + { + return std::holds_alternative(m_data); + } + + bool is_error() const + { + return std::holds_alternative(m_data); + } + + explicit operator bool() const + { + return is_ok(); + } + + T &value() & + { + return std::get(m_data); + } + const T &value() const & + { + return std::get(m_data); + } + T &&value() && + { + return std::get(std::move(m_data)); + } + + const Error &error() const + { + return std::get(m_data); + } + T value_or(T default_value) const { return is_ok() ? std::get(m_data) : std::move(default_value); } - + std::optional ok() const { if (is_ok()) return std::get(m_data); return std::nullopt; } - + std::optional err() const { if (is_error()) return std::get(m_data); return std::nullopt; } - - template - auto map(F&& func) -> Result()))> + + template auto map(F &&func) -> Result()))> { using U = decltype(func(std::declval())); if (is_ok()) return Result(func(std::get(m_data))); return Result(std::get(m_data)); } - - template - auto and_then(F&& func) -> decltype(func(std::declval())) + + template auto and_then(F &&func) -> decltype(func(std::declval())) { if (is_ok()) return func(std::get(m_data)); @@ -145,30 +191,47 @@ public: } }; -template<> -class [[nodiscard]] Result +template <> class [[nodiscard]] Result { -private: + private: std::optional m_error; - -public: - Result() : m_error(std::nullopt) {} - - Result(Error error) : m_error(std::move(error)) {} - - bool is_ok() const { return !m_error.has_value(); } - - bool is_error() const { return m_error.has_value(); } - - explicit operator bool() const { return is_ok(); } - - const Error& error() const { return *m_error; } - - std::optional err() const { return m_error; } + + public: + Result() : m_error(std::nullopt) + { + } + + Result(Error error) : m_error(std::move(error)) + { + } + + bool is_ok() const + { + return !m_error.has_value(); + } + + bool is_error() const + { + return m_error.has_value(); + } + + explicit operator bool() const + { + return is_ok(); + } + + const Error &error() const + { + return *m_error; + } + + std::optional err() const + { + return m_error; + } }; -template -Result Ok(T value) +template Result Ok(T value) { return Result(std::move(value)); } @@ -178,26 +241,22 @@ inline Result Ok() return Result(); } -template -Result Err(Error error) +template Result Err(Error error) { return Result(std::move(error)); } -template -Result Err(error_code code) +template Result Err(error_code code) { return Result(Error(code)); } -template -Result Err(error_code code, std::string message) +template Result Err(error_code code, std::string message) { return Result(Error(code, std::move(message))); } -template -Result Err(error_code code, std::string message, std::string context) +template Result Err(error_code code, std::string message, std::string context) { return Result(Error(code, std::move(message), std::move(context))); } diff --git a/src/core/io/file.hpp b/src/core/io/file.hpp index 0880623..7621635 100644 --- a/src/core/io/file.hpp +++ b/src/core/io/file.hpp @@ -1,10 +1,10 @@ #ifndef CLRSYNC_CORE_IO_FILE_HPP #define CLRSYNC_CORE_IO_FILE_HPP +#include #include #include #include #include -#include using value_type = std::variant; @@ -16,7 +16,10 @@ class file file() = default; file(std::string path) {}; virtual ~file() = default; - virtual Result parse() { return Ok(); }; + virtual Result parse() + { + return Ok(); + }; virtual const std::string get_string_value(const std::string §ion, const std::string &key) const { @@ -35,14 +38,17 @@ class file return {}; } virtual void set_value(const std::string §ion, const std::string &key, - const value_type &value) + const value_type &value) { insert_or_update_value(section, key, value); } virtual void insert_or_update_value(const std::string §ion, const std::string &key, const value_type &value) {}; virtual void remove_section(const std::string §ion) {}; - virtual Result save_file() { return Ok(); }; + virtual Result save_file() + { + return Ok(); + }; }; } // namespace clrsync::core::io #endif \ No newline at end of file diff --git a/src/core/io/toml_file.cpp b/src/core/io/toml_file.cpp index d4b4d93..9b31e5e 100644 --- a/src/core/io/toml_file.cpp +++ b/src/core/io/toml_file.cpp @@ -15,7 +15,7 @@ Result toml_file::parse() { if (!std::filesystem::exists(m_path)) return Err(error_code::file_not_found, "File does not exist", m_path); - + m_file = toml::parse_file(m_path); return Ok(); } @@ -96,10 +96,10 @@ void toml_file::remove_section(const std::string §ion) { toml::table *tbl = m_file.as_table(); auto parts = split(section, '.'); - + if (parts.empty()) return; - + for (size_t i = 0; i < parts.size() - 1; ++i) { auto *sub = (*tbl)[parts[i]].as_table(); @@ -107,26 +107,29 @@ void toml_file::remove_section(const std::string §ion) return; tbl = sub; } - + tbl->erase(parts.back()); } Result toml_file::save_file() { - try { + try + { std::filesystem::create_directories(std::filesystem::path(m_path).parent_path()); - } catch (const std::exception& e) { + } + catch (const std::exception &e) + { return Err(error_code::dir_create_failed, e.what(), m_path); } - + std::ofstream stream(m_path, std::ios::binary); if (!stream) return Err(error_code::file_write_failed, "Failed to open file for writing", m_path); - + stream << m_file; if (!stream) return Err(error_code::file_write_failed, "Failed to write to file", m_path); - + return Ok(); } diff --git a/src/core/io/toml_file.hpp b/src/core/io/toml_file.hpp index 939ee7f..7ba2187 100644 --- a/src/core/io/toml_file.hpp +++ b/src/core/io/toml_file.hpp @@ -1,7 +1,7 @@ #ifndef CLRSYNC_CORE_IO_TOML_FILE_HPP #define CLRSYNC_CORE_IO_TOML_FILE_HPP -#include #include +#include #include #include diff --git a/src/core/palette/color.cpp b/src/core/palette/color.cpp index 81190ca..99c1b73 100644 --- a/src/core/palette/color.cpp +++ b/src/core/palette/color.cpp @@ -111,14 +111,17 @@ void color::from_hex_string(const std::string &str) if (str.empty() || str[0] != '#') throw std::invalid_argument("Invalid hex color format"); - if (str.size() == 7) { + if (str.size() == 7) + { uint32_t rgb = static_cast(std::stoul(str.substr(1), nullptr, 16)); m_hex = (rgb << 8) | 0xFF; } - else if (str.size() == 9) { + else if (str.size() == 9) + { m_hex = static_cast(std::stoul(str.substr(1), nullptr, 16)); } - else { + else + { throw std::invalid_argument("Invalid hex color format"); } } @@ -137,30 +140,38 @@ const std::string color::to_hex_string_with_alpha() const return std::string(buffer); } -std::string color::format(const std::string& field) const +std::string color::format(const std::string &field) const { - auto rgb = to_rgb(); + auto rgb = to_rgb(); auto rgba = to_rgba(); auto hslv = to_hsl(); auto hslav = to_hsla(); - if (field == "hex") return to_hex_string(); - if (field == "hex_stripped") { + if (field == "hex") + return to_hex_string(); + if (field == "hex_stripped") + { auto s = to_hex_string(); return s.substr(1); } - if (field == "hexa") return to_hex_string_with_alpha(); - if (field == "hexa_stripped") { + if (field == "hexa") + return to_hex_string_with_alpha(); + if (field == "hexa_stripped") + { auto s = to_hex_string_with_alpha(); return s.substr(1); } - if (field == "r") return std::to_string(rgb.r); - if (field == "g") return std::to_string(rgb.g); - if (field == "b") return std::to_string(rgb.b); + if (field == "r") + return std::to_string(rgb.r); + if (field == "g") + return std::to_string(rgb.g); + if (field == "b") + return std::to_string(rgb.b); - if (field == "a") { + if (field == "a") + { float af = rgba.a / 255.0f; return std::format("{:.2f}", af); } @@ -169,9 +180,7 @@ std::string color::format(const std::string& field) const return std::format("rgb({},{},{})", rgb.r, rgb.g, rgb.b); if (field == "rgba") - return std::format("rgba({},{},{},{:.2f})", - rgba.r, rgba.g, rgba.b, - rgba.a / 255.0f); + return std::format("rgba({},{},{},{:.2f})", rgba.r, rgba.g, rgba.b, rgba.a / 255.0f); if (field == "h") return std::format("{:.0f}", hslv.h); @@ -185,12 +194,10 @@ std::string color::format(const std::string& field) const return std::format("{:.2f}", hslav.a); if (field == "hsl") - return std::format("hsl({:.0f},{:.2f},{:.2f})", - hslv.h, hslv.s, hslv.l); + return std::format("hsl({:.0f},{:.2f},{:.2f})", hslv.h, hslv.s, hslv.l); if (field == "hsla") - return std::format("hsla({:.0f},{:.2f},{:.2f},{:.2f})", - hslav.h, hslav.s, hslav.l, hslav.a); + return std::format("hsla({:.0f},{:.2f},{:.2f},{:.2f})", hslav.h, hslav.s, hslav.l, hslav.a); throw std::runtime_error("Unknown color format: " + field); } diff --git a/src/core/palette/color.hpp b/src/core/palette/color.hpp index 83df9d1..9c4948d 100644 --- a/src/core/palette/color.hpp +++ b/src/core/palette/color.hpp @@ -60,7 +60,7 @@ class color const std::string to_hex_string_with_alpha() const; - std::string format(const std::string& field) const; + std::string format(const std::string &field) const; void set(uint32_t hex); diff --git a/src/core/palette/color_keys.hpp b/src/core/palette/color_keys.hpp index d1508cb..5debfe5 100644 --- a/src/core/palette/color_keys.hpp +++ b/src/core/palette/color_keys.hpp @@ -3,12 +3,12 @@ #include #include #include -#include #include +#include namespace clrsync::core { -constexpr const char* COLOR_KEYS[] = { +constexpr const char *COLOR_KEYS[] = { // General UI "background", "on_background", @@ -79,31 +79,31 @@ constexpr size_t NUM_COLOR_KEYS = std::size(COLOR_KEYS); inline const std::unordered_map DEFAULT_COLORS = { {"background", 0x111111ff}, {"on_background", 0xd4d4d4ff}, - + {"surface", 0x111111ff}, {"on_surface", 0xd4d4d4ff}, - + {"surface_variant", 0x191919ff}, {"on_surface_variant", 0xd4d4d4ff}, - + {"border_focused", 0x2e2e2eff}, {"border", 0x242424ff}, - + {"foreground", 0xd2d2d2ff}, - + {"cursor", 0xd2d2d2ff}, {"accent", 0x9a8652ff}, - + {"success", 0x668a51ff}, {"info", 0x3a898cff}, {"warning", 0xb47837ff}, {"error", 0xaa4e4aff}, - + {"on_success", 0xd2d2d2ff}, {"on_info", 0xd2d2d2ff}, {"on_warning", 0xd2d2d2ff}, {"on_error", 0xd2d2d2ff}, - + {"editor_background", 0x111111ff}, {"editor_command", 0x3a898cff}, {"editor_comment", 0x849899ff}, @@ -119,7 +119,7 @@ inline const std::unordered_map DEFAULT_COLORS = { {"editor_string", 0x9a8652ff}, {"editor_success", 0x668a51ff}, {"editor_warning", 0xb47837ff}, - + {"base00", 0x111111ff}, {"base01", 0x668a51ff}, {"base02", 0x9a8652ff}, diff --git a/src/core/palette/palette_file.hpp b/src/core/palette/palette_file.hpp index bf6c4ad..4fcb3d9 100644 --- a/src/core/palette/palette_file.hpp +++ b/src/core/palette/palette_file.hpp @@ -26,7 +26,7 @@ template class palette_file if (!m_file->parse()) return false; m_palette.set_name(m_file->get_string_value("general", "name")); - + for (const auto &color_key : COLOR_KEYS) { auto it = DEFAULT_COLORS.find(color_key); @@ -35,7 +35,7 @@ template class palette_file m_palette.set_color(color_key, core::color(it->second)); } } - + for (const auto &color_key : COLOR_KEYS) { auto color_str = m_file->get_string_value("colors", color_key); diff --git a/src/core/theme/template_manager.hpp b/src/core/theme/template_manager.hpp index ab6f3da..6a5f430 100644 --- a/src/core/theme/template_manager.hpp +++ b/src/core/theme/template_manager.hpp @@ -6,12 +6,10 @@ #include #include - namespace clrsync::core { -template -class template_manager +template class template_manager { public: template_manager() = default; diff --git a/src/core/theme/theme_renderer.hpp b/src/core/theme/theme_renderer.hpp index 4c052f9..1e18d22 100644 --- a/src/core/theme/theme_renderer.hpp +++ b/src/core/theme/theme_renderer.hpp @@ -1,9 +1,9 @@ #ifndef CLRSYNC_CORE_THEME_THEME_RENDERER_HPP #define CLRSYNC_CORE_THEME_THEME_RENDERER_HPP #include +#include #include #include -#include #include namespace clrsync::core @@ -26,7 +26,7 @@ template class theme_renderer return Err(error_code::palette_not_found, "Palette not found", theme_name); return apply_palette_to_all_templates(*palette); } - + Result apply_theme_from_path(const std::string &path) { auto palette = m_pal_manager.load_palette_from_file(path); @@ -44,23 +44,24 @@ template class theme_renderer auto &tmpl = t_pair.second; if (!tmpl.enabled()) continue; - + auto load_result = tmpl.load_template(); if (!load_result) return load_result; - + tmpl.apply_palette(pal); - + auto save_result = tmpl.save_output(); if (!save_result) return save_result; - + if (!tmpl.reload_command().empty()) { int result = std::system(tmpl.reload_command().c_str()); if (result != 0) { - std::cerr << "Warning: Command " << tmpl.reload_command() << " failed with code " << result << "\n"; + std::cerr << "Warning: Command " << tmpl.reload_command() + << " failed with code " << result << "\n"; } } } diff --git a/src/core/theme/theme_template.cpp b/src/core/theme/theme_template.cpp index e0290ba..018a570 100644 --- a/src/core/theme/theme_template.cpp +++ b/src/core/theme/theme_template.cpp @@ -47,13 +47,15 @@ Result theme_template::load_template() { if (!std::filesystem::exists(m_template_path)) { - return Err(error_code::template_not_found, "Template file is missing", m_template_path); + return Err(error_code::template_not_found, "Template file is missing", + m_template_path); } - + std::ifstream input(m_template_path, std::ios::binary); if (!input) { - return Err(error_code::template_load_failed, "Failed to open template file", m_template_path); + return Err(error_code::template_load_failed, "Failed to open template file", + m_template_path); } m_template_data.assign(std::istreambuf_iterator(input), std::istreambuf_iterator()); @@ -64,7 +66,7 @@ void theme_template::apply_palette(const core::palette &palette) { m_processed_data = m_template_data; - for (const auto& [key, color] : palette.colors()) + for (const auto &[key, color] : palette.colors()) { // simple replacement: {foreground} replace_all(m_processed_data, "{" + key + "}", color.format("hex")); @@ -96,23 +98,25 @@ Result theme_template::save_output() const { std::filesystem::create_directories(std::filesystem::path(m_output_path).parent_path()); } - catch (const std::exception& e) + catch (const std::exception &e) { return Err(error_code::dir_create_failed, e.what(), m_output_path); } - + std::ofstream output(m_output_path, std::ios::binary); if (!output) { - return Err(error_code::file_write_failed, "Failed to open output file for writing", m_output_path); + return Err(error_code::file_write_failed, "Failed to open output file for writing", + m_output_path); } output << m_processed_data; if (!output) { - return Err(error_code::file_write_failed, "Failed to write to output file", m_output_path); + return Err(error_code::file_write_failed, "Failed to write to output file", + m_output_path); } - + return Ok(); } diff --git a/src/core/theme/theme_template.hpp b/src/core/theme/theme_template.hpp index 50aaf40..b40b72f 100644 --- a/src/core/theme/theme_template.hpp +++ b/src/core/theme/theme_template.hpp @@ -1,8 +1,8 @@ #ifndef clrsync_CORE_IO_THEME_TEMPLATE_HPP #define clrsync_CORE_IO_THEME_TEMPLATE_HPP -#include #include +#include #include namespace clrsync::core diff --git a/src/core/utils.cpp b/src/core/utils.cpp index 86e54ec..066b0b2 100644 --- a/src/core/utils.cpp +++ b/src/core/utils.cpp @@ -1,6 +1,6 @@ #include "utils.hpp" -#include #include +#include namespace clrsync::core { @@ -14,10 +14,10 @@ void print_color_keys() std::string get_default_config_path() { - const char* env_path = std::getenv("CLRSYNC_CONFIG_PATH"); + const char *env_path = std::getenv("CLRSYNC_CONFIG_PATH"); if (env_path && env_path[0] != '\0') return normalize_path(env_path).string(); - + std::filesystem::path home = normalize_path("~"); std::filesystem::path config_path = home / ".config" / "clrsync" / "config.toml"; return config_path.string(); @@ -27,7 +27,7 @@ std::string expand_user(const std::string &path) { if (path.empty() || path[0] != '~') return path; - + if (path.length() == 1 || path[1] == '/' || path[1] == '\\') { #ifdef _WIN32 @@ -37,10 +37,10 @@ std::string expand_user(const std::string &path) #endif if (!home) return path; - + if (path.length() == 1) return std::string(home); - + return std::string(home) + path.substr(1); } return path; diff --git a/src/core/utils.hpp b/src/core/utils.hpp index 3e49da9..8862fec 100644 --- a/src/core/utils.hpp +++ b/src/core/utils.hpp @@ -1,8 +1,8 @@ #ifndef CLRSYNC_CORE_UTILS_HPP #define CLRSYNC_CORE_UTILS_HPP -#include #include +#include #include diff --git a/src/core/version.hpp b/src/core/version.hpp index f031564..999bb54 100644 --- a/src/core/version.hpp +++ b/src/core/version.hpp @@ -6,7 +6,7 @@ namespace clrsync::core { -const std::string GIT_SEMVER = "0.1.6+git.g1a1747a"; +const std::string GIT_SEMVER = "0.1.6+git.g613c2c8"; const std::string version_string(); } // namespace clrsync::core diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 226e79a..a3d79dc 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,23 +1,27 @@ set(GUI_SOURCES main.cpp - color_scheme_editor.cpp - color_table_renderer.cpp - preview_renderer.cpp - theme_applier.cpp - template_editor.cpp - palette_controller.cpp - template_controller.cpp - imgui_helpers.cpp - imgui_helpers.hpp - about_window.cpp - settings_window.cpp - font_loader.cpp - file_browser.cpp - ${CMAKE_SOURCE_DIR}/lib/color_text_edit/TextEditor.cpp + views/color_scheme_editor.cpp + views/color_table_renderer.cpp + views/preview_renderer.cpp + controllers/theme_applier.cpp + views/template_editor.cpp + controllers/palette_controller.cpp + controllers/template_controller.cpp + helpers/imgui_helpers.cpp + helpers/imgui_helpers.hpp + views/about_window.cpp + views/settings_window.cpp + platform/windows/font_loader_windows.cpp + platform/linux/font_loader_linux.cpp + platform/macos/font_loader_macos.cpp + platform/linux/file_browser_linux.cpp + platform/windows/file_browser_windows.cpp + platform/macos/file_browser_macos.mm + ${CMAKE_SOURCE_DIR}/lib/color_text_edit/TextEditor.cpp + platform/linux/font_loader_linux.cpp + platform/macos/font_loader_macos.cpp + platform/windows/font_loader_windows.cpp ) -if(APPLE) - list(APPEND GUI_SOURCES file_browser_macos.mm) -endif() if(WIN32) add_executable(clrsync_gui WIN32 ${GUI_SOURCES}) diff --git a/src/gui/about_window.hpp b/src/gui/about_window.hpp deleted file mode 100644 index ff3676d..0000000 --- a/src/gui/about_window.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef CLRSYNC_GUI_ABOUT_WINDOW_HPP -#define CLRSYNC_GUI_ABOUT_WINDOW_HPP - -#include "core/palette/palette.hpp" - -class about_window -{ -public: - about_window(); - void render(const clrsync::core::palette& pal); - void render() { render(m_default_palette); } - void show() { m_visible = true; } - void hide() { m_visible = false; } - bool is_visible() const { return m_visible; } - -private: - bool m_visible{false}; - clrsync::core::palette m_default_palette; -}; - -#endif // CLRSYNC_GUI_ABOUT_WINDOW_HPP \ No newline at end of file diff --git a/src/gui/color_scheme_editor.hpp b/src/gui/color_scheme_editor.hpp deleted file mode 100644 index 59a41c6..0000000 --- a/src/gui/color_scheme_editor.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CLRSYNC_GUI_COLOR_SCHEME_EDITOR_HPP -#define CLRSYNC_GUI_COLOR_SCHEME_EDITOR_HPP - -#include "palette_controller.hpp" -#include "color_table_renderer.hpp" -#include "preview_renderer.hpp" - -class template_editor; -class settings_window; - -class color_scheme_editor -{ -public: - color_scheme_editor(); - - void render_controls_and_colors(); - void render_preview(); - void set_template_editor(template_editor* editor) { m_template_editor = editor; } - void set_settings_window(settings_window* window) { m_settings_window = window; } - const palette_controller& controller() const { return m_controller; } - -private: - void render_controls(); - void apply_themes(); - void notify_palette_changed(); - - palette_controller m_controller; - color_table_renderer m_color_table; - preview_renderer m_preview; - template_editor* m_template_editor{nullptr}; - settings_window* m_settings_window{nullptr}; - bool m_show_delete_confirmation{false}; -}; - -#endif // CLRSYNC_GUI_COLOR_SCHEME_EDITOR_HPP \ No newline at end of file diff --git a/src/gui/color_table_renderer.hpp b/src/gui/color_table_renderer.hpp deleted file mode 100644 index e518db6..0000000 --- a/src/gui/color_table_renderer.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP -#define CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP - -#include "core/palette/palette.hpp" -#include "palette_controller.hpp" -#include -#include - -class color_table_renderer -{ -public: - using OnColorChangedCallback = std::function; - - void render(const clrsync::core::palette& palette, - palette_controller& controller, - const OnColorChangedCallback& on_changed); - -private: - void render_color_row(const std::string& name, - const clrsync::core::palette& palette, - palette_controller& controller, - const OnColorChangedCallback& on_changed); - - bool matches_filter(const std::string& name) const; - - char m_filter_text[128] = {0}; - bool m_show_only_modified{false}; -}; - -#endif // CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP diff --git a/src/gui/palette_controller.cpp b/src/gui/controllers/palette_controller.cpp similarity index 80% rename from src/gui/palette_controller.cpp rename to src/gui/controllers/palette_controller.cpp index 9e29229..f6153c2 100644 --- a/src/gui/palette_controller.cpp +++ b/src/gui/controllers/palette_controller.cpp @@ -1,4 +1,4 @@ -#include "palette_controller.hpp" +#include "gui/controllers/palette_controller.hpp" #include "core/config/config.hpp" #include "core/theme/theme_renderer.hpp" @@ -7,39 +7,43 @@ palette_controller::palette_controller() m_palette_manager.load_palettes_from_directory( clrsync::core::config::instance().palettes_path()); m_palettes = m_palette_manager.palettes(); - + if (m_palettes.empty()) return; - + auto default_theme = clrsync::core::config::instance().default_theme(); auto it = m_palettes.find(default_theme); - if (it != m_palettes.end()) { + if (it != m_palettes.end()) + { m_current_palette = it->second; - } else { + } + else + { m_current_palette = m_palettes.begin()->second; } } -void palette_controller::select_palette(const std::string& name) +void palette_controller::select_palette(const std::string &name) { auto it = m_palettes.find(name); - if (it != m_palettes.end()) { + if (it != m_palettes.end()) + { m_current_palette = it->second; } } -void palette_controller::create_palette(const std::string& name) +void palette_controller::create_palette(const std::string &name) { clrsync::core::palette new_palette(name); - - for (const auto& [key, hex_value] : clrsync::core::DEFAULT_COLORS) + + for (const auto &[key, hex_value] : clrsync::core::DEFAULT_COLORS) { new_palette.set_color(key, clrsync::core::color(hex_value)); } - + auto dir = clrsync::core::config::instance().palettes_path(); m_palette_manager.save_palette_to_file(new_palette, dir); - + reload_palettes(); m_current_palette = new_palette; } @@ -63,7 +67,7 @@ void palette_controller::apply_current_theme() const (void)theme_renderer.apply_theme(m_current_palette.name()); } -void palette_controller::set_color(const std::string& key, const clrsync::core::color& color) +void palette_controller::set_color(const std::string &key, const clrsync::core::color &color) { m_current_palette.set_color(key, color); } diff --git a/src/gui/palette_controller.hpp b/src/gui/controllers/palette_controller.hpp similarity index 63% rename from src/gui/palette_controller.hpp rename to src/gui/controllers/palette_controller.hpp index d35051e..6916696 100644 --- a/src/gui/palette_controller.hpp +++ b/src/gui/controllers/palette_controller.hpp @@ -6,23 +6,30 @@ #include #include -class palette_controller { -public: +class palette_controller +{ + public: palette_controller(); - const clrsync::core::palette& current_palette() const { return m_current_palette; } - const std::unordered_map& palettes() const { return m_palettes; } + const clrsync::core::palette ¤t_palette() const + { + return m_current_palette; + } + const std::unordered_map &palettes() const + { + return m_palettes; + } - void select_palette(const std::string& name); - void create_palette(const std::string& name); + void select_palette(const std::string &name); + void create_palette(const std::string &name); void save_current_palette(); void delete_current_palette(); void apply_current_theme() const; - void set_color(const std::string& key, const clrsync::core::color& color); - -private: + void set_color(const std::string &key, const clrsync::core::color &color); + + private: void reload_palettes(); - + clrsync::core::palette_manager m_palette_manager; std::unordered_map m_palettes; clrsync::core::palette m_current_palette; diff --git a/src/gui/template_controller.cpp b/src/gui/controllers/template_controller.cpp similarity index 73% rename from src/gui/template_controller.cpp rename to src/gui/controllers/template_controller.cpp index 68a6f8b..f66a795 100644 --- a/src/gui/template_controller.cpp +++ b/src/gui/controllers/template_controller.cpp @@ -1,4 +1,4 @@ -#include "template_controller.hpp" +#include "gui/controllers/template_controller.hpp" #include "core/config/config.hpp" template_controller::template_controller() @@ -6,46 +6,52 @@ template_controller::template_controller() m_templates = m_template_manager.templates(); } -void template_controller::set_template_enabled(const std::string& key, bool enabled) +void template_controller::set_template_enabled(const std::string &key, bool enabled) { auto it = m_templates.find(key); - if (it != m_templates.end()) { + if (it != m_templates.end()) + { it->second.set_enabled(enabled); (void)clrsync::core::config::instance().update_template(key, it->second); } } -void template_controller::set_template_input_path(const std::string& key, const std::string& path) +void template_controller::set_template_input_path(const std::string &key, const std::string &path) { auto it = m_templates.find(key); - if (it != m_templates.end()) { + if (it != m_templates.end()) + { it->second.set_template_path(path); (void)clrsync::core::config::instance().update_template(key, it->second); } } -void template_controller::set_template_output_path(const std::string& key, const std::string& path) +void template_controller::set_template_output_path(const std::string &key, const std::string &path) { auto it = m_templates.find(key); - if (it != m_templates.end()) { + if (it != m_templates.end()) + { it->second.set_output_path(path); (void)clrsync::core::config::instance().update_template(key, it->second); } } -void template_controller::set_template_reload_command(const std::string& key, const std::string& cmd) +void template_controller::set_template_reload_command(const std::string &key, + const std::string &cmd) { auto it = m_templates.find(key); - if (it != m_templates.end()) { + if (it != m_templates.end()) + { it->second.set_reload_command(cmd); (void)clrsync::core::config::instance().update_template(key, it->second); } } -bool template_controller::remove_template(const std::string& key) +bool template_controller::remove_template(const std::string &key) { auto result = clrsync::core::config::instance().remove_template(key); - if (result) { + if (result) + { m_templates.erase(key); return true; } diff --git a/src/gui/template_controller.hpp b/src/gui/controllers/template_controller.hpp similarity index 53% rename from src/gui/template_controller.hpp rename to src/gui/controllers/template_controller.hpp index e01484f..02b9fa6 100644 --- a/src/gui/template_controller.hpp +++ b/src/gui/controllers/template_controller.hpp @@ -1,24 +1,29 @@ #ifndef CLRSYNC_GUI_TEMPLATE_CONTROLLER_HPP #define CLRSYNC_GUI_TEMPLATE_CONTROLLER_HPP -#include "core/theme/theme_template.hpp" -#include "core/theme/template_manager.hpp" #include "core/io/toml_file.hpp" +#include "core/theme/template_manager.hpp" +#include "core/theme/theme_template.hpp" #include #include -class template_controller { -public: +class template_controller +{ + public: template_controller(); - [[nodiscard]] const std::unordered_map& templates() const { return m_templates; } - void set_template_enabled(const std::string& key, bool enabled); - void set_template_input_path(const std::string& key, const std::string& path); - void set_template_output_path(const std::string& key, const std::string& path); - void set_template_reload_command(const std::string& key, const std::string& cmd); - bool remove_template(const std::string& key); + [[nodiscard]] const std::unordered_map &templates() + const + { + return m_templates; + } + void set_template_enabled(const std::string &key, bool enabled); + void set_template_input_path(const std::string &key, const std::string &path); + void set_template_output_path(const std::string &key, const std::string &path); + void set_template_reload_command(const std::string &key, const std::string &cmd); + bool remove_template(const std::string &key); void refresh(); - -private: + + private: clrsync::core::template_manager m_template_manager; std::unordered_map m_templates; }; diff --git a/src/gui/theme_applier.cpp b/src/gui/controllers/theme_applier.cpp similarity index 83% rename from src/gui/theme_applier.cpp rename to src/gui/controllers/theme_applier.cpp index ed8e967..e6523e5 100644 --- a/src/gui/theme_applier.cpp +++ b/src/gui/controllers/theme_applier.cpp @@ -1,10 +1,10 @@ -#include "theme_applier.hpp" +#include "gui/controllers/theme_applier.hpp" #include "imgui.h" namespace theme_applier { -static uint32_t get_color_u32(const clrsync::core::palette& current, const std::string &key) +static uint32_t get_color_u32(const clrsync::core::palette ¤t, const std::string &key) { const auto &col = current.get_color(key); const uint32_t hex = col.hex(); @@ -16,7 +16,7 @@ static uint32_t get_color_u32(const clrsync::core::palette& current, const std:: return (a << 24) | (b << 16) | (g << 8) | r; } -void apply_to_editor(TextEditor& editor, const clrsync::core::palette& current) +void apply_to_editor(TextEditor &editor, const clrsync::core::palette ¤t) { auto palette = editor.GetPalette(); @@ -26,32 +26,40 @@ void apply_to_editor(TextEditor& editor, const clrsync::core::palette& current) palette[int(TextEditor::PaletteIndex::String)] = get_color_u32(current, "editor_string"); palette[int(TextEditor::PaletteIndex::CharLiteral)] = get_color_u32(current, "editor_string"); palette[int(TextEditor::PaletteIndex::Punctuation)] = get_color_u32(current, "editor_main"); - palette[int(TextEditor::PaletteIndex::Preprocessor)] = get_color_u32(current, "editor_emphasis"); + palette[int(TextEditor::PaletteIndex::Preprocessor)] = + get_color_u32(current, "editor_emphasis"); palette[int(TextEditor::PaletteIndex::Identifier)] = get_color_u32(current, "editor_main"); palette[int(TextEditor::PaletteIndex::KnownIdentifier)] = get_color_u32(current, "editor_link"); - palette[int(TextEditor::PaletteIndex::PreprocIdentifier)] = get_color_u32(current, "editor_link"); + palette[int(TextEditor::PaletteIndex::PreprocIdentifier)] = + get_color_u32(current, "editor_link"); palette[int(TextEditor::PaletteIndex::Comment)] = get_color_u32(current, "editor_comment"); - palette[int(TextEditor::PaletteIndex::MultiLineComment)] = get_color_u32(current, "editor_comment"); + palette[int(TextEditor::PaletteIndex::MultiLineComment)] = + get_color_u32(current, "editor_comment"); - palette[int(TextEditor::PaletteIndex::Background)] = get_color_u32(current, "editor_background"); + palette[int(TextEditor::PaletteIndex::Background)] = + get_color_u32(current, "editor_background"); palette[int(TextEditor::PaletteIndex::Cursor)] = get_color_u32(current, "cursor"); palette[int(TextEditor::PaletteIndex::Selection)] = get_color_u32(current, "editor_selected"); palette[int(TextEditor::PaletteIndex::ErrorMarker)] = get_color_u32(current, "editor_error"); palette[int(TextEditor::PaletteIndex::Breakpoint)] = get_color_u32(current, "editor_error"); - palette[int(TextEditor::PaletteIndex::LineNumber)] = get_color_u32(current, "editor_line_number"); - - palette[int(TextEditor::PaletteIndex::CurrentLineFill)] = get_color_u32(current, "surface_variant"); - palette[int(TextEditor::PaletteIndex::CurrentLineFillInactive)] = get_color_u32(current, "surface"); + palette[int(TextEditor::PaletteIndex::LineNumber)] = + get_color_u32(current, "editor_line_number"); - palette[int(TextEditor::PaletteIndex::CurrentLineEdge)] = get_color_u32(current, "border_focused"); + palette[int(TextEditor::PaletteIndex::CurrentLineFill)] = + get_color_u32(current, "surface_variant"); + palette[int(TextEditor::PaletteIndex::CurrentLineFillInactive)] = + get_color_u32(current, "surface"); + + palette[int(TextEditor::PaletteIndex::CurrentLineEdge)] = + get_color_u32(current, "border_focused"); editor.SetPalette(palette); } -void apply_to_imgui(const clrsync::core::palette& current) +void apply_to_imgui(const clrsync::core::palette ¤t) { auto getColor = [&](const std::string &key) -> ImVec4 { const auto &col = current.get_color(key); @@ -72,7 +80,7 @@ void apply_to_imgui(const clrsync::core::palette& current) const ImVec4 fgInactive = getColor("editor_inactive"); const ImVec4 accent = getColor("accent"); const ImVec4 border = getColor("border"); - + const ImVec4 error = getColor("error"); const ImVec4 onError = getColor("on_error"); const ImVec4 success = getColor("success"); @@ -138,7 +146,8 @@ void apply_to_imgui(const clrsync::core::palette& current) ImVec4(border.x * 0.7f, border.y * 0.7f, border.z * 0.7f, border.w); style.Colors[ImGuiCol_TableRowBg] = ImVec4(0, 0, 0, 0); - style.Colors[ImGuiCol_TableRowBgAlt] = ImVec4(onSurfaceVariant.x, onSurfaceVariant.y, onSurfaceVariant.z, 0.06f); + style.Colors[ImGuiCol_TableRowBgAlt] = + ImVec4(onSurfaceVariant.x, onSurfaceVariant.y, onSurfaceVariant.z, 0.06f); style.Colors[ImGuiCol_Separator] = border; style.Colors[ImGuiCol_SeparatorHovered] = accent; diff --git a/src/gui/theme_applier.hpp b/src/gui/controllers/theme_applier.hpp similarity index 57% rename from src/gui/theme_applier.hpp rename to src/gui/controllers/theme_applier.hpp index 6d4c463..7cc5f67 100644 --- a/src/gui/theme_applier.hpp +++ b/src/gui/controllers/theme_applier.hpp @@ -1,13 +1,13 @@ #ifndef CLRSYNC_GUI_THEME_APPLIER_HPP #define CLRSYNC_GUI_THEME_APPLIER_HPP -#include "core/palette/palette.hpp" #include "color_text_edit/TextEditor.h" +#include "core/palette/palette.hpp" namespace theme_applier { - void apply_to_imgui(const clrsync::core::palette& pal); - void apply_to_editor(TextEditor& editor, const clrsync::core::palette& pal); -} +void apply_to_imgui(const clrsync::core::palette &pal); +void apply_to_editor(TextEditor &editor, const clrsync::core::palette &pal); +} // namespace theme_applier #endif // CLRSYNC_GUI_THEME_APPLIER_HPP diff --git a/src/gui/file_browser.cpp b/src/gui/file_browser.cpp deleted file mode 100644 index db32ac0..0000000 --- a/src/gui/file_browser.cpp +++ /dev/null @@ -1,263 +0,0 @@ -#include "file_browser.hpp" -#include - -#ifdef _WIN32 -#include -#include -#include -#include -#include - -namespace file_dialogs { - -std::string open_file_dialog(const std::string& title, - const std::string& initial_path, - const std::vector& filters) { - OPENFILENAMEA ofn; - char file[MAX_PATH] = ""; - - std::string filter_str = "All Files (*.*)\0*.*\0"; - - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = GetActiveWindow(); - ofn.lpstrFile = file; - ofn.nMaxFile = sizeof(file); - ofn.lpstrFilter = filter_str.c_str(); - ofn.nFilterIndex = 1; - ofn.lpstrTitle = title.c_str(); - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; - - if (!initial_path.empty() && std::filesystem::exists(initial_path)) { - std::filesystem::path p(initial_path); - if (std::filesystem::is_directory(p)) { - ofn.lpstrInitialDir = initial_path.c_str(); - } else { - std::string dir = p.parent_path().string(); - std::string name = p.filename().string(); - ofn.lpstrInitialDir = dir.c_str(); - strncpy(file, name.c_str(), sizeof(file) - 1); - } - } - - if (GetOpenFileNameA(&ofn)) { - return std::string(file); - } - return ""; -} - -std::string save_file_dialog(const std::string& title, - const std::string& initial_path, - const std::vector& filters) { - OPENFILENAMEA ofn; - char file[MAX_PATH] = ""; - - std::string filter_str = "All Files\0*.*\0\0"; - - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = GetActiveWindow(); - ofn.lpstrFile = file; - ofn.nMaxFile = sizeof(file); - ofn.lpstrFilter = filter_str.c_str(); - ofn.nFilterIndex = 1; - ofn.lpstrTitle = title.c_str(); - ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; - - if (!initial_path.empty()) { - std::filesystem::path p(initial_path); - if (std::filesystem::exists(p) && std::filesystem::is_directory(p)) { - ofn.lpstrInitialDir = initial_path.c_str(); - } else { - std::string dir = p.parent_path().string(); - std::string name = p.filename().string(); - if (std::filesystem::exists(dir)) { - ofn.lpstrInitialDir = dir.c_str(); - strncpy(file, name.c_str(), sizeof(file) - 1); - } - } - } - - if (GetSaveFileNameA(&ofn)) { - return std::string(file); - } - return ""; -} - -std::string select_folder_dialog(const std::string& title, - const std::string& initial_path) { - IFileOpenDialog *pFileOpen; - - HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, - IID_IFileOpenDialog, reinterpret_cast(&pFileOpen)); - - if (SUCCEEDED(hr)) { - DWORD dwFlags; - if (SUCCEEDED(pFileOpen->GetOptions(&dwFlags))) { - pFileOpen->SetOptions(dwFlags | FOS_PICKFOLDERS); - } - - std::wstring wtitle(title.begin(), title.end()); - pFileOpen->SetTitle(wtitle.c_str()); - - if (!initial_path.empty() && std::filesystem::exists(initial_path)) { - IShellItem *psi = NULL; - std::wstring winitial(initial_path.begin(), initial_path.end()); - hr = SHCreateItemFromParsingName(winitial.c_str(), NULL, IID_IShellItem, (void**)&psi); - if (SUCCEEDED(hr)) { - pFileOpen->SetFolder(psi); - psi->Release(); - } - } - - hr = pFileOpen->Show(GetActiveWindow()); - - if (SUCCEEDED(hr)) { - IShellItem *pItem; - hr = pFileOpen->GetResult(&pItem); - if (SUCCEEDED(hr)) { - PWSTR pszFilePath; - hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); - - if (SUCCEEDED(hr)) { - std::wstring wpath(pszFilePath); - std::string result(wpath.begin(), wpath.end()); - CoTaskMemFree(pszFilePath); - pItem->Release(); - pFileOpen->Release(); - return result; - } - pItem->Release(); - } - } - pFileOpen->Release(); - } - - return ""; -} - -} - -#elif !defined(__APPLE__) - -#include - -namespace file_dialogs { - -std::string open_file_dialog(const std::string& title, - const std::string& initial_path, - const std::vector& filters) { - if (!gtk_init_check(nullptr, nullptr)) { - return ""; - } - - GtkFileChooserNative *native = gtk_file_chooser_native_new( - title.c_str(), - nullptr, - GTK_FILE_CHOOSER_ACTION_OPEN, - "_Open", - "_Cancel"); - - GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); - - if (!initial_path.empty()) { - std::filesystem::path p(initial_path); - if (std::filesystem::exists(p)) { - if (std::filesystem::is_directory(p)) { - gtk_file_chooser_set_current_folder(chooser, initial_path.c_str()); - } else { - gtk_file_chooser_set_current_folder(chooser, p.parent_path().c_str()); - gtk_file_chooser_set_current_name(chooser, p.filename().c_str()); - } - } - } - - std::string result; - if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) { - char* filename = gtk_file_chooser_get_filename(chooser); - if (filename) { - result = filename; - g_free(filename); - } - } - - g_object_unref(native); - while (gtk_events_pending()) gtk_main_iteration(); - return result; -} - -std::string save_file_dialog(const std::string& title, - const std::string& initial_path, - const std::vector& filters) { - if (!gtk_init_check(nullptr, nullptr)) { - return ""; - } - - GtkFileChooserNative *native = gtk_file_chooser_native_new( - title.c_str(), - nullptr, - GTK_FILE_CHOOSER_ACTION_SAVE, - "_Save", - "_Cancel"); - - GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); - gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE); - - if (!initial_path.empty()) { - std::filesystem::path p(initial_path); - if (std::filesystem::exists(p.parent_path())) { - gtk_file_chooser_set_current_folder(chooser, p.parent_path().c_str()); - gtk_file_chooser_set_current_name(chooser, p.filename().c_str()); - } - } - - std::string result; - if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) { - char* filename = gtk_file_chooser_get_filename(chooser); - if (filename) { - result = filename; - g_free(filename); - } - } - - g_object_unref(native); - while (gtk_events_pending()) gtk_main_iteration(); - return result; -} - -std::string select_folder_dialog(const std::string& title, - const std::string& initial_path) { - if (!gtk_init_check(nullptr, nullptr)) { - return ""; - } - - GtkFileChooserNative *native = gtk_file_chooser_native_new( - title.c_str(), - nullptr, - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, - "_Select", - "_Cancel"); - - GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); - - if (!initial_path.empty() && std::filesystem::exists(initial_path)) { - gtk_file_chooser_set_current_folder(chooser, initial_path.c_str()); - } - - std::string result; - if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) { - char* filename = gtk_file_chooser_get_filename(chooser); - if (filename) { - result = filename; - g_free(filename); - } - } - - g_object_unref(native); - while (gtk_events_pending()) gtk_main_iteration(); - return result; -} - -} - -#endif \ No newline at end of file diff --git a/src/gui/file_browser.hpp b/src/gui/file_browser.hpp deleted file mode 100644 index 46d75c0..0000000 --- a/src/gui/file_browser.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CLRSYNC_GUI_FILE_BROWSER_HPP -#define CLRSYNC_GUI_FILE_BROWSER_HPP - -#include -#include - -namespace file_dialogs { - std::string open_file_dialog(const std::string& title = "Open File", - const std::string& initial_path = "", - const std::vector& filters = {}); - - std::string save_file_dialog(const std::string& title = "Save File", - const std::string& initial_path = "", - const std::vector& filters = {}); - - std::string select_folder_dialog(const std::string& title = "Select Folder", - const std::string& initial_path = ""); -} - -#endif // CLRSYNC_GUI_FILE_BROWSER_HPP \ No newline at end of file diff --git a/src/gui/font_loader.cpp b/src/gui/font_loader.cpp deleted file mode 100644 index 9ca1b8a..0000000 --- a/src/gui/font_loader.cpp +++ /dev/null @@ -1,333 +0,0 @@ -#include "font_loader.hpp" -#include "core/config/config.hpp" -#include "imgui_internal.h" -#include -#include - -#if defined(_WIN32) - -#include -#include -#include - -static std::string search_registry_for_font(HKEY root_key, const char* subkey, const std::string& font_name_lower, const char* default_font_dir) -{ - HKEY hKey; - LONG result = RegOpenKeyExA(root_key, subkey, 0, KEY_READ, &hKey); - - if (result != ERROR_SUCCESS) - return {}; - - char value_name[512]; - BYTE value_data[512]; - DWORD value_name_size, value_data_size, type; - DWORD index = 0; - - std::string found_path; - - while (true) - { - value_name_size = sizeof(value_name); - value_data_size = sizeof(value_data); - - result = RegEnumValueA(hKey, index++, value_name, &value_name_size, nullptr, &type, value_data, &value_data_size); - - if (result != ERROR_SUCCESS) - break; - - if (type != REG_SZ) - continue; - - std::string reg_font_name = value_name; - std::transform(reg_font_name.begin(), reg_font_name.end(), reg_font_name.begin(), ::tolower); - - std::string reg_font_name_clean = reg_font_name; - size_t type_pos = reg_font_name_clean.find(" ("); - if (type_pos != std::string::npos) - reg_font_name_clean = reg_font_name_clean.substr(0, type_pos); - - if (reg_font_name_clean == font_name_lower) - { - std::string font_file = reinterpret_cast(value_data); - - // If path is not absolute, prepend default font directory - if (font_file.find(":\\") == std::string::npos) - { - found_path = std::string(default_font_dir) + "\\" + font_file; - } - else - { - found_path = font_file; - } - break; - } - } - - RegCloseKey(hKey); - return found_path; -} - -std::string font_loader::find_font_windows(const char* font_name) -{ - std::string font_name_lower = font_name; - std::transform(font_name_lower.begin(), font_name_lower.end(), font_name_lower.begin(), ::tolower); - - char windows_dir[MAX_PATH]; - GetWindowsDirectoryA(windows_dir, MAX_PATH); - std::string system_fonts_dir = std::string(windows_dir) + "\\Fonts"; - - // First, try system-wide fonts (HKEY_LOCAL_MACHINE) - std::string path = search_registry_for_font( - HKEY_LOCAL_MACHINE, - "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", - font_name_lower, - system_fonts_dir.c_str() - ); - - if (!path.empty()) - return path; - - // If not found, try per-user fonts (HKEY_CURRENT_USER) - char local_appdata[MAX_PATH]; - if (GetEnvironmentVariableA("LOCALAPPDATA", local_appdata, MAX_PATH) > 0) - { - std::string user_fonts_dir = std::string(local_appdata) + "\\Microsoft\\Windows\\Fonts"; - - path = search_registry_for_font( - HKEY_CURRENT_USER, - "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", - font_name_lower, - user_fonts_dir.c_str() - ); - } - - return path; -} -#endif - -#if defined(__APPLE__) - -#include - -std::vector font_loader::load_font_macos(const char* font_name) -{ - std::vector out; - - CFStringRef cf_name = CFStringCreateWithCString(nullptr, font_name, kCFStringEncodingUTF8); - if (!cf_name) - return out; - - CTFontDescriptorRef desc = CTFontDescriptorCreateWithNameAndSize(cf_name, 12); - CFRelease(cf_name); - - if (!desc) - return out; - - CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(desc, kCTFontURLAttribute); - CFRelease(desc); - - if (!url) - return out; - - CFDataRef data = nullptr; - Boolean success = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &data, nullptr, nullptr, nullptr); - CFRelease(url); - - if (success && data) - { - CFIndex size = CFDataGetLength(data); - if (size > 100) - { - out.resize(size); - CFDataGetBytes(data, CFRangeMake(0, size), out.data()); - } - CFRelease(data); - } - - return out; -} - -#endif - -#if !defined(_WIN32) && !defined(__APPLE__) - -#include - -std::string font_loader::find_font_linux(const char* font_name) -{ - FcInit(); - - FcPattern* pattern = FcNameParse(reinterpret_cast(font_name)); - if (!pattern) - return {}; - - FcConfigSubstitute(nullptr, pattern, FcMatchPattern); - FcDefaultSubstitute(pattern); - - FcResult result; - FcPattern* match = FcFontMatch(nullptr, pattern, &result); - - std::string out; - - if (match) - { - FcChar8* file = nullptr; - if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch) - out = reinterpret_cast(file); - - FcPatternDestroy(match); - } - - FcPatternDestroy(pattern); - return out; -} - -#endif - -std::string font_loader::find_font_path(const char* font_name) -{ -#if defined(_WIN32) - return find_font_windows(font_name); - -#elif defined(__APPLE__) - (void)font_name; - return {}; - -#else - return find_font_linux(font_name); - -#endif -} - -ImFont* font_loader::load_font(const char* font_name, float size_px) -{ -#if defined(__APPLE__) - std::vector buf = load_font_macos(font_name); - if (buf.empty()) - return nullptr; - - return ImGui::GetIO().Fonts->AddFontFromMemoryTTF( - buf.data(), - static_cast(buf.size()), - size_px - ); - -#else - std::string path = find_font_path(font_name); - if (path.empty()) - return nullptr; - - float scale = ImGui::GetIO().DisplayFramebufferScale.y; - return ImGui::GetIO().Fonts->AddFontFromFileTTF( - path.c_str(), - size_px * scale - ); -#endif -} - -void font_loader::push_default_font() -{ - ImGui::PushFont(ImGui::GetDefaultFont(), clrsync::core::config::instance().font_size()); -} -void font_loader::pop_font() -{ - ImGui::PopFont(); -} - -std::vector font_loader::get_system_fonts() -{ - std::vector fonts; - -#if defined(_WIN32) - auto enumerate_registry_fonts = [&fonts](HKEY root_key, const char* subkey) - { - HKEY hKey; - if (RegOpenKeyExA(root_key, subkey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) - return; - - char value_name[512]; - DWORD value_name_size; - DWORD index = 0; - - while (true) - { - value_name_size = sizeof(value_name); - LONG result = RegEnumValueA(hKey, index++, value_name, &value_name_size, nullptr, nullptr, nullptr, nullptr); - - if (result != ERROR_SUCCESS) - break; - - std::string font_name = value_name; - size_t pos = font_name.find(" ("); - if (pos != std::string::npos) - font_name = font_name.substr(0, pos); - - if (std::find(fonts.begin(), fonts.end(), font_name) == fonts.end()) - fonts.push_back(font_name); - } - - RegCloseKey(hKey); - }; - - enumerate_registry_fonts(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"); - enumerate_registry_fonts(HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"); - -#elif defined(__APPLE__) - CTFontCollectionRef collection = CTFontCollectionCreateFromAvailableFonts(nullptr); - if (collection) - { - CFArrayRef fontDescriptors = CTFontCollectionCreateMatchingFontDescriptors(collection); - CFRelease(collection); - - if (fontDescriptors) - { - CFIndex count = CFArrayGetCount(fontDescriptors); - for (CFIndex i = 0; i < count; i++) - { - CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(fontDescriptors, i); - CFStringRef fontName = (CFStringRef)CTFontDescriptorCopyAttribute(descriptor, kCTFontDisplayNameAttribute); - - if (fontName) - { - char buffer[256]; - if (CFStringGetCString(fontName, buffer, sizeof(buffer), kCFStringEncodingUTF8)) - { - std::string font_name = buffer; - if (std::find(fonts.begin(), fonts.end(), font_name) == fonts.end()) - fonts.push_back(font_name); - } - CFRelease(fontName); - } - } - CFRelease(fontDescriptors); - } - } - -#else - FcInit(); - FcPattern* pattern = FcPatternCreate(); - FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, nullptr); - FcFontSet* fs = FcFontList(nullptr, pattern, os); - - if (fs) - { - for (int i = 0; i < fs->nfont; i++) - { - FcChar8* family = nullptr; - if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch) - { - std::string font_name = reinterpret_cast(family); - if (std::find(fonts.begin(), fonts.end(), font_name) == fonts.end()) - fonts.push_back(font_name); - } - } - FcFontSetDestroy(fs); - } - - FcObjectSetDestroy(os); - FcPatternDestroy(pattern); -#endif - - std::sort(fonts.begin(), fonts.end()); - return fonts; -} diff --git a/src/gui/imgui_helpers.cpp b/src/gui/helpers/imgui_helpers.cpp similarity index 75% rename from src/gui/imgui_helpers.cpp rename to src/gui/helpers/imgui_helpers.cpp index 8f9e7e6..b7034e5 100644 --- a/src/gui/imgui_helpers.cpp +++ b/src/gui/helpers/imgui_helpers.cpp @@ -2,7 +2,7 @@ #include #include "GLFW/glfw3.h" -#include "gui/settings_window.hpp" +#include "gui/views/settings_window.hpp" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" @@ -10,12 +10,12 @@ #include "imgui_internal.h" -GLFWwindow * init_glfw() +GLFWwindow *init_glfw() { - glfwSetErrorCallback([](int error, const char* description) { + glfwSetErrorCallback([](int error, const char *description) { std::cerr << "GLFW Error " << error << ": " << description << std::endl; }); - + if (!glfwInit()) { std::cerr << "Failed to initialize GLFW\n"; @@ -23,14 +23,14 @@ GLFWwindow * init_glfw() } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - #ifdef __APPLE__ +#ifdef __APPLE__ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - #else +#else glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - #endif +#endif glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); - GLFWwindow* w = glfwCreateWindow(1280, 720, "clrsync", nullptr, nullptr); + GLFWwindow *w = glfwCreateWindow(1280, 720, "clrsync", nullptr, nullptr); if (!w) { std::cerr << "Failed to create GLFW window\n"; @@ -42,22 +42,22 @@ GLFWwindow * init_glfw() return w; } -void init_imgui(GLFWwindow* window, const std::string& ini_path) +void init_imgui(GLFWwindow *window, const std::string &ini_path) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO &io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.IniFilename = ini_path.c_str(); ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); - #ifdef __APPLE__ +#ifdef __APPLE__ ImGui_ImplOpenGL3_Init("#version 150"); - #else +#else ImGui_ImplOpenGL3_Init("#version 120"); - #endif +#endif } void begin_frame() @@ -67,7 +67,7 @@ void begin_frame() ImGui::NewFrame(); } -void render_menu_bar(about_window* about, settings_window* settings) +void render_menu_bar(about_window *about, settings_window *settings) { if (ImGui::BeginMainMenuBar()) { @@ -99,25 +99,21 @@ void render_menu_bar(about_window* about, settings_window* settings) } } -void setup_main_dockspace(bool& first_time) +void setup_main_dockspace(bool &first_time) { - const ImGuiViewport* viewport = ImGui::GetMainViewport(); + const ImGuiViewport *viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); - constexpr ImGuiWindowFlags flags = - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoBringToFrontOnFocus | - ImGuiWindowFlags_NoNavFocus | - ImGuiWindowFlags_MenuBar; + constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoBringToFrontOnFocus | + ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar; ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::Begin("MainDockSpace", nullptr, flags); ImGui::PopStyleVar(3); @@ -134,7 +130,7 @@ void setup_main_dockspace(bool& first_time) ImGuiID center, right; ImGui::DockBuilderSplitNode(dockspace_id, ImGuiDir_Right, 0.5f, &right, ¢er); - ImGuiDockNode* center_node = ImGui::DockBuilderGetNode(center); + ImGuiDockNode *center_node = ImGui::DockBuilderGetNode(center); if (center_node) { center_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; @@ -143,15 +139,15 @@ void setup_main_dockspace(bool& first_time) ImGui::DockBuilderDockWindow("Color Schemes", right); ImGui::DockBuilderDockWindow("Color Preview", center); ImGui::DockBuilderDockWindow("Templates", center); - + ImGui::DockBuilderFinish(dockspace_id); } - ImGui::DockSpace(dockspace_id, ImVec2{0,0}, ImGuiDockNodeFlags_None); + ImGui::DockSpace(dockspace_id, ImVec2{0, 0}, ImGuiDockNodeFlags_None); ImGui::End(); } -void end_frame(GLFWwindow* window) +void end_frame(GLFWwindow *window) { ImGui::Render(); int w, h; @@ -164,7 +160,7 @@ void end_frame(GLFWwindow* window) glfwSwapBuffers(window); } -void shutdown(GLFWwindow* window) +void shutdown(GLFWwindow *window) { ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); @@ -173,24 +169,25 @@ void shutdown(GLFWwindow* window) glfwTerminate(); } -namespace palette_utils +namespace palette_utils { -ImVec4 get_color(const clrsync::core::palette& pal, const std::string& key, const std::string& fallback) +ImVec4 get_color(const clrsync::core::palette &pal, const std::string &key, + const std::string &fallback) { auto colors = pal.colors(); if (colors.empty()) return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - + auto it = colors.find(key); if (it == colors.end() && !fallback.empty()) { it = colors.find(fallback); } - + if (it != colors.end()) { - const auto& col = it->second; + const auto &col = it->second; const uint32_t hex = col.hex(); const float r = ((hex >> 24) & 0xFF) / 255.0f; const float g = ((hex >> 16) & 0xFF) / 255.0f; @@ -201,21 +198,22 @@ ImVec4 get_color(const clrsync::core::palette& pal, const std::string& key, cons return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); } -uint32_t get_color_u32(const clrsync::core::palette& pal, const std::string& key, const std::string& fallback) +uint32_t get_color_u32(const clrsync::core::palette &pal, const std::string &key, + const std::string &fallback) { auto colors = pal.colors(); if (colors.empty()) return 0xFFFFFFFF; - + auto it = colors.find(key); if (it == colors.end() && !fallback.empty()) { it = colors.find(fallback); } - + if (it != colors.end()) { - const auto& col = it->second; + const auto &col = it->second; const uint32_t hex = col.hex(); const uint32_t r = (hex >> 24) & 0xFF; const uint32_t g = (hex >> 16) & 0xFF; @@ -226,21 +224,23 @@ uint32_t get_color_u32(const clrsync::core::palette& pal, const std::string& key return 0xFFFFFFFF; } -bool render_delete_confirmation_popup(const std::string& popup_title, const std::string& item_name, - const std::string& item_type, const clrsync::core::palette& pal, - const std::function& on_delete) +bool render_delete_confirmation_popup(const std::string &popup_title, const std::string &item_name, + const std::string &item_type, + const clrsync::core::palette &pal, + const std::function &on_delete) { bool result = false; if (ImGui::BeginPopupModal(popup_title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { ImVec4 warning_color = get_color(pal, "warning", "accent"); - ImGui::TextColored(warning_color, "Are you sure you want to delete '%s'?", item_name.c_str()); + ImGui::TextColored(warning_color, "Are you sure you want to delete '%s'?", + item_name.c_str()); ImGui::Text("This action cannot be undone."); - + ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); - + float button_width = 120.0f; float total_width = 2.0f * button_width + ImGui::GetStyle().ItemSpacing.x; float window_width = ImGui::GetContentRegionAvail().x; @@ -252,7 +252,7 @@ bool render_delete_confirmation_popup(const std::string& popup_title, const std: result = true; ImGui::CloseCurrentPopup(); } - + ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(button_width, 0))) { @@ -263,4 +263,4 @@ bool render_delete_confirmation_popup(const std::string& popup_title, const std: return result; } -} \ No newline at end of file +} // namespace palette_utils \ No newline at end of file diff --git a/src/gui/helpers/imgui_helpers.hpp b/src/gui/helpers/imgui_helpers.hpp new file mode 100644 index 0000000..a57e8f4 --- /dev/null +++ b/src/gui/helpers/imgui_helpers.hpp @@ -0,0 +1,42 @@ +#ifndef CLRSYNC_IMGUI_HELPERS_HPP +#define CLRSYNC_IMGUI_HELPERS_HPP + +#include "core/palette/palette.hpp" +#include "gui/views/about_window.hpp" +#include "imgui.h" +#include +#include + +struct GLFWwindow; +class settings_window; + +GLFWwindow *init_glfw(); +void init_imgui(GLFWwindow *window, const std::string &ini_path); +void begin_frame(); +void setup_main_dockspace(bool &first_time); +void end_frame(GLFWwindow *window); +void shutdown(GLFWwindow *window); +void render_menu_bar(about_window *about, settings_window *settings); + +namespace palette_utils +{ +ImVec4 get_color(const clrsync::core::palette &pal, const std::string &key, + const std::string &fallback = ""); +uint32_t get_color_u32(const clrsync::core::palette &pal, const std::string &key, + const std::string &fallback = ""); +bool render_delete_confirmation_popup(const std::string &popup_title, const std::string &item_name, + const std::string &item_type, + const clrsync::core::palette &pal, + const std::function &on_delete); +} // namespace palette_utils + +namespace imgui_helpers +{ +inline ImVec4 get_palette_color(const clrsync::core::palette &pal, const std::string &key, + const std::string &fallback = "") +{ + return palette_utils::get_color(pal, key, fallback); +} +} // namespace imgui_helpers + +#endif // CLRSYNC_IMGUI_HELPERS_HPP diff --git a/src/gui/imgui_helpers.hpp b/src/gui/imgui_helpers.hpp deleted file mode 100644 index f4a7f85..0000000 --- a/src/gui/imgui_helpers.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CLRSYNC_IMGUI_HELPERS_HPP -#define CLRSYNC_IMGUI_HELPERS_HPP - -#include "gui/about_window.hpp" -#include "core/palette/palette.hpp" -#include "imgui.h" -#include -#include - -struct GLFWwindow; -class settings_window; - -GLFWwindow * init_glfw(); -void init_imgui(GLFWwindow* window, const std::string& ini_path); -void begin_frame(); -void setup_main_dockspace(bool& first_time); -void end_frame(GLFWwindow* window); -void shutdown(GLFWwindow* window); -void render_menu_bar(about_window* about, settings_window* settings); - -namespace palette_utils -{ - ImVec4 get_color(const clrsync::core::palette& pal, const std::string& key, const std::string& fallback = ""); - uint32_t get_color_u32(const clrsync::core::palette& pal, const std::string& key, const std::string& fallback = ""); - bool render_delete_confirmation_popup(const std::string& popup_title, const std::string& item_name, - const std::string& item_type, const clrsync::core::palette& pal, - const std::function& on_delete); -} - -namespace imgui_helpers -{ - inline ImVec4 get_palette_color(const clrsync::core::palette& pal, const std::string& key, const std::string& fallback = "") { - return palette_utils::get_color(pal, key, fallback); - } -} - -#endif // CLRSYNC_IMGUI_HELPERS_HPP diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 2517f1c..2d08745 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -3,30 +3,30 @@ #include #include - #include "core/config/config.hpp" +#include "core/error.hpp" #include "core/io/toml_file.hpp" #include "core/utils.hpp" -#include "core/error.hpp" -#include "color_scheme_editor.hpp" -#include "gui/font_loader.hpp" -#include "gui/settings_window.hpp" -#include "imgui_helpers.hpp" -#include "template_editor.hpp" -#include "about_window.hpp" +#include "gui/helpers/imgui_helpers.hpp" +#include "gui/platform/font_loader.hpp" +#include "gui/views/about_window.hpp" +#include "gui/views/color_scheme_editor.hpp" +#include "gui/views/settings_window.hpp" +#include "gui/views/template_editor.hpp" - -int main(int, char**) +int main(int, char **) { auto config_path = clrsync::core::get_default_config_path(); auto conf = std::make_unique(config_path); - + auto init_result = clrsync::core::config::instance().initialize(std::move(conf)); if (!init_result) { std::cerr << "Fatal error: " << init_result.error().description() << std::endl; - std::cerr << "Hint: Set CLRSYNC_CONFIG_PATH environment variable or ensure config exists at: " << config_path << std::endl; + std::cerr + << "Hint: Set CLRSYNC_CONFIG_PATH environment variable or ensure config exists at: " + << config_path << std::endl; return 1; } @@ -34,27 +34,37 @@ int main(int, char**) static std::string ini_path = (base.parent_path() / "layout.ini").string(); bool first_time = !std::filesystem::exists(ini_path); - GLFWwindow* window = init_glfw(); - if (!window) return 1; + GLFWwindow *window = init_glfw(); + if (!window) + return 1; printf("GLFV Version: %s\n", glfwGetVersionString()); std::cout << "GLFW runtime platform: "; - switch (glfwGetPlatform()) { - case GLFW_PLATFORM_WAYLAND: std::cout << "Wayland\n"; break; - case GLFW_PLATFORM_X11: std::cout << "X11\n"; break; - case GLFW_PLATFORM_COCOA: std::cout << "Cocoa\n"; break; - case GLFW_PLATFORM_WIN32: std::cout << "Win32\n"; break; - default: std::cout << "Unknown\n"; + switch (glfwGetPlatform()) + { + case GLFW_PLATFORM_WAYLAND: + std::cout << "Wayland\n"; + break; + case GLFW_PLATFORM_X11: + std::cout << "X11\n"; + break; + case GLFW_PLATFORM_COCOA: + std::cout << "Cocoa\n"; + break; + case GLFW_PLATFORM_WIN32: + std::cout << "Win32\n"; + break; + default: + std::cout << "Unknown\n"; } - init_imgui(window, ini_path); font_loader loader; - ImFont* font = - loader.load_font(clrsync::core::config::instance().font().c_str(), clrsync::core::config::instance().font_size()); + ImFont *font = loader.load_font(clrsync::core::config::instance().font().c_str(), + clrsync::core::config::instance().font_size()); if (font) ImGui::GetIO().FontDefault = font; @@ -63,7 +73,7 @@ int main(int, char**) template_editor templateEditor; about_window aboutWindow; settings_window settingsWindow; - + colorEditor.set_template_editor(&templateEditor); colorEditor.set_settings_window(&settingsWindow); templateEditor.apply_current_palette(colorEditor.controller().current_palette()); @@ -76,7 +86,7 @@ int main(int, char**) begin_frame(); render_menu_bar(&aboutWindow, &settingsWindow); - setup_main_dockspace(first_time); + setup_main_dockspace(first_time); templateEditor.render(); colorEditor.render_controls_and_colors(); colorEditor.render_preview(); diff --git a/src/gui/platform/file_browser.hpp b/src/gui/platform/file_browser.hpp new file mode 100644 index 0000000..dcab9a3 --- /dev/null +++ b/src/gui/platform/file_browser.hpp @@ -0,0 +1,21 @@ +#ifndef CLRSYNC_GUI_FILE_BROWSER_HPP +#define CLRSYNC_GUI_FILE_BROWSER_HPP + +#include +#include + +namespace file_dialogs +{ +std::string open_file_dialog(const std::string &title = "Open File", + const std::string &initial_path = "", + const std::vector &filters = {}); + +std::string save_file_dialog(const std::string &title = "Save File", + const std::string &initial_path = "", + const std::vector &filters = {}); + +std::string select_folder_dialog(const std::string &title = "Select Folder", + const std::string &initial_path = ""); +} // namespace file_dialogs + +#endif // CLRSYNC_GUI_FILE_BROWSER_HPP \ No newline at end of file diff --git a/src/gui/font_loader.hpp b/src/gui/platform/font_loader.hpp similarity index 56% rename from src/gui/font_loader.hpp rename to src/gui/platform/font_loader.hpp index 326c1e4..d3fc228 100644 --- a/src/gui/font_loader.hpp +++ b/src/gui/platform/font_loader.hpp @@ -1,30 +1,30 @@ #ifndef CLRSYNC_GUI_SYSTEM_FONT_LOADER_HPP #define CLRSYNC_GUI_SYSTEM_FONT_LOADER_HPP +#include #include #include -#include class font_loader { -public: + public: font_loader() = default; - ImFont* load_font(const char* font_name, float size_px); + ImFont *load_font(const char *font_name, float size_px); void push_default_font(); void pop_font(); - + std::vector get_system_fonts(); -private: - std::string find_font_path(const char* font_name); + private: + std::string find_font_path(const char *font_name); #if defined(_WIN32) - static std::string find_font_windows(const char* font_name); + static std::string find_font_windows(const char *font_name); #elif defined(__APPLE__) - std::vector load_font_macos(const char* font_name); + std::vector load_font_macos(const char *font_name); #else - std::string find_font_linux(const char* font_name); + std::string find_font_linux(const char *font_name); #endif }; diff --git a/src/gui/platform/linux/file_browser_linux.cpp b/src/gui/platform/linux/file_browser_linux.cpp new file mode 100644 index 0000000..f35eaa7 --- /dev/null +++ b/src/gui/platform/linux/file_browser_linux.cpp @@ -0,0 +1,132 @@ +#ifdef __linux__ + +#include "gui/platform/file_browser.hpp" +#include + +namespace file_dialogs +{ + +std::string open_file_dialog(const std::string &title, const std::string &initial_path, + const std::vector &filters) +{ + if (!gtk_init_check(nullptr, nullptr)) + { + return ""; + } + + GtkFileChooserNative *native = gtk_file_chooser_native_new( + title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_OPEN, "_Open", "_Cancel"); + + GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); + + if (!initial_path.empty()) + { + std::filesystem::path p(initial_path); + if (std::filesystem::exists(p)) + { + if (std::filesystem::is_directory(p)) + { + gtk_file_chooser_set_current_folder(chooser, initial_path.c_str()); + } + else + { + gtk_file_chooser_set_current_folder(chooser, p.parent_path().c_str()); + gtk_file_chooser_set_current_name(chooser, p.filename().c_str()); + } + } + } + + std::string result; + if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) + { + char *filename = gtk_file_chooser_get_filename(chooser); + if (filename) + { + result = filename; + g_free(filename); + } + } + + g_object_unref(native); + while (gtk_events_pending()) + gtk_main_iteration(); + return result; +} + +std::string save_file_dialog(const std::string &title, const std::string &initial_path, + const std::vector &filters) +{ + if (!gtk_init_check(nullptr, nullptr)) + { + return ""; + } + + GtkFileChooserNative *native = gtk_file_chooser_native_new( + title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SAVE, "_Save", "_Cancel"); + + GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); + gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE); + + if (!initial_path.empty()) + { + std::filesystem::path p(initial_path); + if (std::filesystem::exists(p.parent_path())) + { + gtk_file_chooser_set_current_folder(chooser, p.parent_path().c_str()); + gtk_file_chooser_set_current_name(chooser, p.filename().c_str()); + } + } + + std::string result; + if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) + { + char *filename = gtk_file_chooser_get_filename(chooser); + if (filename) + { + result = filename; + g_free(filename); + } + } + + g_object_unref(native); + while (gtk_events_pending()) + gtk_main_iteration(); + return result; +} + +std::string select_folder_dialog(const std::string &title, const std::string &initial_path) +{ + if (!gtk_init_check(nullptr, nullptr)) + { + return ""; + } + + GtkFileChooserNative *native = gtk_file_chooser_native_new( + title.c_str(), nullptr, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "_Select", "_Cancel"); + + GtkFileChooser *chooser = GTK_FILE_CHOOSER(native); + + if (!initial_path.empty() && std::filesystem::exists(initial_path)) + { + gtk_file_chooser_set_current_folder(chooser, initial_path.c_str()); + } + + std::string result; + if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) + { + char *filename = gtk_file_chooser_get_filename(chooser); + if (filename) + { + result = filename; + g_free(filename); + } + } + + g_object_unref(native); + while (gtk_events_pending()) + gtk_main_iteration(); + return result; +} + +} // namespace file_dialogs +#endif \ No newline at end of file diff --git a/src/gui/platform/linux/font_loader_linux.cpp b/src/gui/platform/linux/font_loader_linux.cpp new file mode 100644 index 0000000..e55cc13 --- /dev/null +++ b/src/gui/platform/linux/font_loader_linux.cpp @@ -0,0 +1,95 @@ +#ifdef __linux__ + +#include "core/config/config.hpp" +#include "gui/platform/font_loader.hpp" +#include "imgui_internal.h" +#include +#include +#include + +std::string font_loader::find_font_linux(const char *font_name) +{ + FcInit(); + + FcPattern *pattern = FcNameParse(reinterpret_cast(font_name)); + if (!pattern) + return {}; + + FcConfigSubstitute(nullptr, pattern, FcMatchPattern); + FcDefaultSubstitute(pattern); + + FcResult result; + FcPattern *match = FcFontMatch(nullptr, pattern, &result); + + std::string out; + + if (match) + { + FcChar8 *file = nullptr; + if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch) + out = reinterpret_cast(file); + + FcPatternDestroy(match); + } + + FcPatternDestroy(pattern); + return out; +} + +std::string font_loader::find_font_path(const char *font_name) +{ + return find_font_linux(font_name); +} + +ImFont *font_loader::load_font(const char *font_name, float size_px) +{ + std::string path = find_font_path(font_name); + if (path.empty()) + return nullptr; + + float scale = ImGui::GetIO().DisplayFramebufferScale.y; + return ImGui::GetIO().Fonts->AddFontFromFileTTF(path.c_str(), size_px * scale); +} + +void font_loader::push_default_font() +{ + ImGui::PushFont(ImGui::GetDefaultFont(), clrsync::core::config::instance().font_size()); +} + +void font_loader::pop_font() +{ + ImGui::PopFont(); +} + +std::vector font_loader::get_system_fonts() +{ + std::vector fonts; + + FcInit(); + FcPattern *pattern = FcPatternCreate(); + FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, nullptr); + FcFontSet *fs = FcFontList(nullptr, pattern, os); + + if (fs) + { + for (int i = 0; i < fs->nfont; i++) + { + FcChar8 *family = nullptr; + if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch) + { + std::string font_name = reinterpret_cast(family); + if (std::find(fonts.begin(), fonts.end(), font_name) == fonts.end()) + fonts.push_back(font_name); + } + } + FcFontSetDestroy(fs); + } + + FcObjectSetDestroy(os); + FcPatternDestroy(pattern); + + std::sort(fonts.begin(), fonts.end()); + return fonts; +} + +#endif diff --git a/src/gui/file_browser_macos.mm b/src/gui/platform/macos/file_browser_macos.mm similarity index 98% rename from src/gui/file_browser_macos.mm rename to src/gui/platform/macos/file_browser_macos.mm index 9d737bb..d1ad98f 100644 --- a/src/gui/file_browser_macos.mm +++ b/src/gui/platform/macos/file_browser_macos.mm @@ -1,7 +1,6 @@ -#include "file_browser.hpp" -#include - #ifdef __APPLE__ +#include "gui/platform/file_browser.hpp" +#include #include namespace file_dialogs { @@ -76,5 +75,4 @@ std::string select_folder_dialog(const std::string& title, } } - #endif \ No newline at end of file diff --git a/src/gui/platform/macos/font_loader_macos.cpp b/src/gui/platform/macos/font_loader_macos.cpp new file mode 100644 index 0000000..7dda739 --- /dev/null +++ b/src/gui/platform/macos/font_loader_macos.cpp @@ -0,0 +1,115 @@ +#ifdef __APPLE__ + +#include "core/config/config.hpp" +#include "gui/platform/font_loader.hpp" +#include "imgui_internal.h" +#include +#include +#include + +std::vector font_loader::load_font_macos(const char *font_name) +{ + std::vector out; + + CFStringRef cf_name = CFStringCreateWithCString(nullptr, font_name, kCFStringEncodingUTF8); + if (!cf_name) + return out; + + CTFontDescriptorRef desc = CTFontDescriptorCreateWithNameAndSize(cf_name, 12); + CFRelease(cf_name); + + if (!desc) + return out; + + CFURLRef url = (CFURLRef)CTFontDescriptorCopyAttribute(desc, kCTFontURLAttribute); + CFRelease(desc); + + if (!url) + return out; + + CFDataRef data = nullptr; + Boolean success = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &data, + nullptr, nullptr, nullptr); + CFRelease(url); + + if (success && data) + { + CFIndex size = CFDataGetLength(data); + if (size > 100) + { + out.resize(size); + CFDataGetBytes(data, CFRangeMake(0, size), out.data()); + } + CFRelease(data); + } + + return out; +} + +std::string font_loader::find_font_path(const char *font_name) +{ + (void)font_name; + return {}; +} + +ImFont *font_loader::load_font(const char *font_name, float size_px) +{ + std::vector buf = load_font_macos(font_name); + if (buf.empty()) + return nullptr; + + return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(buf.data(), static_cast(buf.size()), + size_px); +} + +void font_loader::push_default_font() +{ + ImGui::PushFont(ImGui::GetDefaultFont(), clrsync::core::config::instance().font_size()); +} + +void font_loader::pop_font() +{ + ImGui::PopFont(); +} + +std::vector font_loader::get_system_fonts() +{ + std::vector fonts; + + CTFontCollectionRef collection = CTFontCollectionCreateFromAvailableFonts(nullptr); + if (collection) + { + CFArrayRef fontDescriptors = CTFontCollectionCreateMatchingFontDescriptors(collection); + CFRelease(collection); + + if (fontDescriptors) + { + CFIndex count = CFArrayGetCount(fontDescriptors); + for (CFIndex i = 0; i < count; i++) + { + CTFontDescriptorRef descriptor = + (CTFontDescriptorRef)CFArrayGetValueAtIndex(fontDescriptors, i); + CFStringRef fontName = (CFStringRef)CTFontDescriptorCopyAttribute( + descriptor, kCTFontDisplayNameAttribute); + + if (fontName) + { + char buffer[256]; + if (CFStringGetCString(fontName, buffer, sizeof(buffer), kCFStringEncodingUTF8)) + { + std::string font_name = buffer; + if (std::find(fonts.begin(), fonts.end(), font_name) == fonts.end()) + fonts.push_back(font_name); + } + CFRelease(fontName); + } + } + CFRelease(fontDescriptors); + } + } + + std::sort(fonts.begin(), fonts.end()); + return fonts; +} + +#endif \ No newline at end of file diff --git a/src/gui/platform/windows/file_browser_windows.cpp b/src/gui/platform/windows/file_browser_windows.cpp new file mode 100644 index 0000000..1ab75da --- /dev/null +++ b/src/gui/platform/windows/file_browser_windows.cpp @@ -0,0 +1,160 @@ +#ifdef _WIN32 + +#include "gui/platform/file_browser.hpp" +#include + +#include +#include +#include +#include + +namespace file_dialogs +{ + +std::string open_file_dialog(const std::string &title, const std::string &initial_path, + const std::vector &filters) +{ + OPENFILENAMEA ofn; + char file[MAX_PATH] = ""; + + std::string filter_str = "All Files (*.*)\0*.*\0"; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = GetActiveWindow(); + ofn.lpstrFile = file; + ofn.nMaxFile = sizeof(file); + ofn.lpstrFilter = filter_str.c_str(); + ofn.nFilterIndex = 1; + ofn.lpstrTitle = title.c_str(); + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; + + if (!initial_path.empty() && std::filesystem::exists(initial_path)) + { + std::filesystem::path p(initial_path); + if (std::filesystem::is_directory(p)) + { + ofn.lpstrInitialDir = initial_path.c_str(); + } + else + { + std::string dir = p.parent_path().string(); + std::string name = p.filename().string(); + ofn.lpstrInitialDir = dir.c_str(); + strncpy(file, name.c_str(), sizeof(file) - 1); + } + } + + if (GetOpenFileNameA(&ofn)) + { + return std::string(file); + } + return ""; +} + +std::string save_file_dialog(const std::string &title, const std::string &initial_path, + const std::vector &filters) +{ + OPENFILENAMEA ofn; + char file[MAX_PATH] = ""; + + std::string filter_str = "All Files\0*.*\0\0"; + + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = GetActiveWindow(); + ofn.lpstrFile = file; + ofn.nMaxFile = sizeof(file); + ofn.lpstrFilter = filter_str.c_str(); + ofn.nFilterIndex = 1; + ofn.lpstrTitle = title.c_str(); + ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + + if (!initial_path.empty()) + { + std::filesystem::path p(initial_path); + if (std::filesystem::exists(p) && std::filesystem::is_directory(p)) + { + ofn.lpstrInitialDir = initial_path.c_str(); + } + else + { + std::string dir = p.parent_path().string(); + std::string name = p.filename().string(); + if (std::filesystem::exists(dir)) + { + ofn.lpstrInitialDir = dir.c_str(); + strncpy(file, name.c_str(), sizeof(file) - 1); + } + } + } + + if (GetSaveFileNameA(&ofn)) + { + return std::string(file); + } + return ""; +} + +std::string select_folder_dialog(const std::string &title, const std::string &initial_path) +{ + IFileOpenDialog *pFileOpen; + + HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_IFileOpenDialog, + reinterpret_cast(&pFileOpen)); + + if (SUCCEEDED(hr)) + { + DWORD dwFlags; + if (SUCCEEDED(pFileOpen->GetOptions(&dwFlags))) + { + pFileOpen->SetOptions(dwFlags | FOS_PICKFOLDERS); + } + + std::wstring wtitle(title.begin(), title.end()); + pFileOpen->SetTitle(wtitle.c_str()); + + if (!initial_path.empty() && std::filesystem::exists(initial_path)) + { + IShellItem *psi = NULL; + std::wstring winitial(initial_path.begin(), initial_path.end()); + hr = SHCreateItemFromParsingName(winitial.c_str(), NULL, IID_IShellItem, (void **)&psi); + if (SUCCEEDED(hr)) + { + pFileOpen->SetFolder(psi); + psi->Release(); + } + } + + hr = pFileOpen->Show(GetActiveWindow()); + + if (SUCCEEDED(hr)) + { + IShellItem *pItem; + hr = pFileOpen->GetResult(&pItem); + if (SUCCEEDED(hr)) + { + PWSTR pszFilePath; + hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); + + if (SUCCEEDED(hr)) + { + std::wstring wpath(pszFilePath); + std::string result(wpath.begin(), wpath.end()); + CoTaskMemFree(pszFilePath); + pItem->Release(); + pFileOpen->Release(); + return result; + } + pItem->Release(); + } + } + pFileOpen->Release(); + } + + return ""; +} + +} // namespace file_dialogs + +#endif \ No newline at end of file diff --git a/src/gui/platform/windows/font_loader_windows.cpp b/src/gui/platform/windows/font_loader_windows.cpp new file mode 100644 index 0000000..3ef8230 --- /dev/null +++ b/src/gui/platform/windows/font_loader_windows.cpp @@ -0,0 +1,168 @@ +#include "core/config/config.hpp" +#include "gui/platform/font_loader.hpp" +#include "imgui_internal.h" +#include +#include +#include +#include + +static std::string search_registry_for_font(HKEY root_key, const char *subkey, + const std::string &font_name_lower, + const char *default_font_dir) +{ + HKEY hKey; + LONG result = RegOpenKeyExA(root_key, subkey, 0, KEY_READ, &hKey); + + if (result != ERROR_SUCCESS) + return {}; + + char value_name[512]; + BYTE value_data[512]; + DWORD value_name_size, value_data_size, type; + DWORD index = 0; + + std::string found_path; + + while (true) + { + value_name_size = sizeof(value_name); + value_data_size = sizeof(value_data); + + result = RegEnumValueA(hKey, index++, value_name, &value_name_size, nullptr, &type, + value_data, &value_data_size); + + if (result != ERROR_SUCCESS) + break; + + if (type != REG_SZ) + continue; + + std::string reg_font_name = value_name; + std::transform(reg_font_name.begin(), reg_font_name.end(), reg_font_name.begin(), + ::tolower); + + std::string reg_font_name_clean = reg_font_name; + size_t type_pos = reg_font_name_clean.find(" ("); + if (type_pos != std::string::npos) + reg_font_name_clean = reg_font_name_clean.substr(0, type_pos); + + if (reg_font_name_clean == font_name_lower) + { + std::string font_file = reinterpret_cast(value_data); + + // If path is not absolute, prepend default font directory + if (font_file.find(":\\") == std::string::npos) + { + found_path = std::string(default_font_dir) + "\\" + font_file; + } + else + { + found_path = font_file; + } + break; + } + } + + RegCloseKey(hKey); + return found_path; +} + +std::string font_loader::find_font_windows(const char *font_name) +{ + std::string font_name_lower = font_name; + std::transform(font_name_lower.begin(), font_name_lower.end(), font_name_lower.begin(), + ::tolower); + + char windows_dir[MAX_PATH]; + GetWindowsDirectoryA(windows_dir, MAX_PATH); + std::string system_fonts_dir = std::string(windows_dir) + "\\Fonts"; + + // First, try system-wide fonts (HKEY_LOCAL_MACHINE) + std::string path = search_registry_for_font( + HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", + font_name_lower, system_fonts_dir.c_str()); + + if (!path.empty()) + return path; + + // If not found, try per-user fonts (HKEY_CURRENT_USER) + char local_appdata[MAX_PATH]; + if (GetEnvironmentVariableA("LOCALAPPDATA", local_appdata, MAX_PATH) > 0) + { + std::string user_fonts_dir = std::string(local_appdata) + "\\Microsoft\\Windows\\Fonts"; + + path = search_registry_for_font(HKEY_CURRENT_USER, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", + font_name_lower, user_fonts_dir.c_str()); + } + + return path; +} + +std::string font_loader::find_font_path(const char *font_name) +{ + return find_font_windows(font_name); +} + +ImFont *font_loader::load_font(const char *font_name, float size_px) +{ + std::string path = find_font_path(font_name); + if (path.empty()) + return nullptr; + + float scale = ImGui::GetIO().DisplayFramebufferScale.y; + return ImGui::GetIO().Fonts->AddFontFromFileTTF(path.c_str(), size_px * scale); +} + +void font_loader::push_default_font() +{ + ImGui::PushFont(ImGui::GetDefaultFont(), clrsync::core::config::instance().font_size()); +} + +void font_loader::pop_font() +{ + ImGui::PopFont(); +} + +std::vector font_loader::get_system_fonts() +{ + std::vector fonts; + + auto enumerate_registry_fonts = [&fonts](HKEY root_key, const char *subkey) { + HKEY hKey; + if (RegOpenKeyExA(root_key, subkey, 0, KEY_READ, &hKey) != ERROR_SUCCESS) + return; + + char value_name[512]; + DWORD value_name_size; + DWORD index = 0; + + while (true) + { + value_name_size = sizeof(value_name); + LONG result = RegEnumValueA(hKey, index++, value_name, &value_name_size, nullptr, + nullptr, nullptr, nullptr); + + if (result != ERROR_SUCCESS) + break; + + std::string font_name = value_name; + size_t pos = font_name.find(" ("); + if (pos != std::string::npos) + font_name = font_name.substr(0, pos); + + if (std::find(fonts.begin(), fonts.end(), font_name) == fonts.end()) + fonts.push_back(font_name); + } + + RegCloseKey(hKey); + }; + + enumerate_registry_fonts(HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"); + enumerate_registry_fonts(HKEY_CURRENT_USER, + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"); + + std::sort(fonts.begin(), fonts.end()); + return fonts; +} diff --git a/src/gui/about_window.cpp b/src/gui/views/about_window.cpp similarity index 97% rename from src/gui/about_window.cpp rename to src/gui/views/about_window.cpp index f503217..1d9aaf8 100644 --- a/src/gui/about_window.cpp +++ b/src/gui/views/about_window.cpp @@ -1,13 +1,13 @@ #include "about_window.hpp" #include "core/version.hpp" -#include "imgui_helpers.hpp" +#include "gui/helpers/imgui_helpers.hpp" #include "imgui.h" about_window::about_window() { } -void about_window::render(const clrsync::core::palette& pal) +void about_window::render(const clrsync::core::palette &pal) { if (!m_visible) return; diff --git a/src/gui/views/about_window.hpp b/src/gui/views/about_window.hpp new file mode 100644 index 0000000..dee73ae --- /dev/null +++ b/src/gui/views/about_window.hpp @@ -0,0 +1,33 @@ +#ifndef CLRSYNC_GUI_ABOUT_WINDOW_HPP +#define CLRSYNC_GUI_ABOUT_WINDOW_HPP + +#include "core/palette/palette.hpp" + +class about_window +{ + public: + about_window(); + void render(const clrsync::core::palette &pal); + void render() + { + render(m_default_palette); + } + void show() + { + m_visible = true; + } + void hide() + { + m_visible = false; + } + bool is_visible() const + { + return m_visible; + } + + private: + bool m_visible{false}; + clrsync::core::palette m_default_palette; +}; + +#endif // CLRSYNC_GUI_ABOUT_WINDOW_HPP \ No newline at end of file diff --git a/src/gui/color_scheme_editor.cpp b/src/gui/views/color_scheme_editor.cpp similarity index 97% rename from src/gui/color_scheme_editor.cpp rename to src/gui/views/color_scheme_editor.cpp index de3a7a8..818a6ec 100644 --- a/src/gui/color_scheme_editor.cpp +++ b/src/gui/views/color_scheme_editor.cpp @@ -1,13 +1,12 @@ #include "color_scheme_editor.hpp" +#include "gui/controllers/theme_applier.hpp" +#include "gui/helpers/imgui_helpers.hpp" #include "imgui.h" -#include "imgui_helpers.hpp" -#include "template_editor.hpp" #include "settings_window.hpp" -#include "theme_applier.hpp" +#include "template_editor.hpp" #include #include - color_scheme_editor::color_scheme_editor() { const auto ¤t = m_controller.current_palette(); @@ -161,10 +160,8 @@ void color_scheme_editor::render_controls() ImGui::SameLine(); auto error = palette_utils::get_color(current, "error"); - auto error_hover = ImVec4(error.x * 1.1f, error.y * 1.1f, error.z * 1.1f, - error.w); - auto error_active = ImVec4(error.x * 0.8f, error.y * 0.8f, error.z * 0.8f, - error.w); + auto error_hover = ImVec4(error.x * 1.1f, error.y * 1.1f, error.z * 1.1f, error.w); + auto error_active = ImVec4(error.x * 0.8f, error.y * 0.8f, error.z * 0.8f, error.w); auto on_error = palette_utils::get_color(current, "on_error"); ImGui::PushStyleColor(ImGuiCol_Button, error); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_hover); diff --git a/src/gui/views/color_scheme_editor.hpp b/src/gui/views/color_scheme_editor.hpp new file mode 100644 index 0000000..8297595 --- /dev/null +++ b/src/gui/views/color_scheme_editor.hpp @@ -0,0 +1,44 @@ +#ifndef CLRSYNC_GUI_COLOR_SCHEME_EDITOR_HPP +#define CLRSYNC_GUI_COLOR_SCHEME_EDITOR_HPP + +#include "gui/controllers/palette_controller.hpp" +#include "gui/views/color_table_renderer.hpp" +#include "gui/views/preview_renderer.hpp" + +class template_editor; +class settings_window; + +class color_scheme_editor +{ + public: + color_scheme_editor(); + + void render_controls_and_colors(); + void render_preview(); + void set_template_editor(template_editor *editor) + { + m_template_editor = editor; + } + void set_settings_window(settings_window *window) + { + m_settings_window = window; + } + const palette_controller &controller() const + { + return m_controller; + } + + private: + void render_controls(); + void apply_themes(); + void notify_palette_changed(); + + palette_controller m_controller; + color_table_renderer m_color_table; + preview_renderer m_preview; + template_editor *m_template_editor{nullptr}; + settings_window *m_settings_window{nullptr}; + bool m_show_delete_confirmation{false}; +}; + +#endif // CLRSYNC_GUI_COLOR_SCHEME_EDITOR_HPP \ No newline at end of file diff --git a/src/gui/color_table_renderer.cpp b/src/gui/views/color_table_renderer.cpp similarity index 71% rename from src/gui/color_table_renderer.cpp rename to src/gui/views/color_table_renderer.cpp index 65b7c47..bc31bbf 100644 --- a/src/gui/color_table_renderer.cpp +++ b/src/gui/views/color_table_renderer.cpp @@ -1,46 +1,46 @@ -#include "color_table_renderer.hpp" -#include "imgui_helpers.hpp" +#include "gui/views/color_table_renderer.hpp" +#include "gui/helpers/imgui_helpers.hpp" #include "imgui.h" #include #include #include -bool color_table_renderer::matches_filter(const std::string& name) const +bool color_table_renderer::matches_filter(const std::string &name) const { if (m_filter_text[0] == '\0') return true; - + std::string filter_lower = m_filter_text; std::string name_lower = name; - + std::transform(filter_lower.begin(), filter_lower.end(), filter_lower.begin(), [](unsigned char c) { return std::tolower(c); }); std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), [](unsigned char c) { return std::tolower(c); }); - + return name_lower.find(filter_lower) != std::string::npos; } void color_table_renderer::render_color_row(const std::string &name, - const clrsync::core::palette& current, - palette_controller& controller, - const OnColorChangedCallback& on_changed) + const clrsync::core::palette ¤t, + palette_controller &controller, + const OnColorChangedCallback &on_changed) { if (!matches_filter(name)) return; - + const clrsync::core::color &col = current.get_color(name); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); const float key_col_width = ImGui::GetContentRegionAvail().x; - + ImVec4 text_color = palette_utils::get_color(current, "info", "accent"); ImGui::PushStyleColor(ImGuiCol_Text, text_color); const bool copied = ImGui::Selectable(name.c_str(), false, 0, ImVec2(key_col_width, 0.0f)); ImGui::PopStyleColor(); - + if (ImGui::IsItemHovered()) { ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); @@ -101,9 +101,9 @@ void color_table_renderer::render_color_row(const std::string &name, ImGui::PopID(); } -void color_table_renderer::render(const clrsync::core::palette& current, - palette_controller& controller, - const OnColorChangedCallback& on_changed) +void color_table_renderer::render(const clrsync::core::palette ¤t, + palette_controller &controller, + const OnColorChangedCallback &on_changed) { if (current.colors().empty()) { @@ -113,13 +113,13 @@ void color_table_renderer::render(const clrsync::core::palette& current, } ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8, 6)); - + ImGui::Text("Filter:"); ImGui::SameLine(); ImGui::SetNextItemWidth(200); - bool filter_changed = ImGui::InputTextWithHint("##color_filter", "Search colors...", - m_filter_text, sizeof(m_filter_text)); - + bool filter_changed = ImGui::InputTextWithHint("##color_filter", "Search colors...", + m_filter_text, sizeof(m_filter_text)); + if (m_filter_text[0] != '\0') { ImGui::SameLine(); @@ -131,14 +131,15 @@ void color_table_renderer::render(const clrsync::core::palette& current, if (ImGui::IsItemHovered()) ImGui::SetTooltip("Clear filter"); } - + ImGui::PopStyleVar(); - + ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); - auto draw_table = [&](const char *title, const char* id, const std::vector &keys) { + auto draw_table = [&](const char *title, const char *id, + const std::vector &keys) { bool has_matches = false; for (auto *k : keys) { @@ -148,18 +149,20 @@ void color_table_renderer::render(const clrsync::core::palette& current, break; } } - + if (!has_matches) return; - + ImGui::PushStyleColor(ImGuiCol_Text, palette_utils::get_color(current, "accent")); - bool header_open = ImGui::TreeNodeEx(title, ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAvailWidth); + bool header_open = ImGui::TreeNodeEx(title, ImGuiTreeNodeFlags_DefaultOpen | + ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::PopStyleColor(); - + if (header_open) { - if (ImGui::BeginTable(id, 3, - ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) + if (ImGui::BeginTable(id, 3, + ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | + ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 160.0f); ImGui::TableSetupColumn("HEX", ImGuiTableColumnFlags_WidthFixed, 95.0f); @@ -177,23 +180,23 @@ void color_table_renderer::render(const clrsync::core::palette& current, ImGui::Spacing(); }; - draw_table("General UI", "##general_ui", {"background", "on_background", "surface", "on_surface", - "surface_variant", "on_surface_variant", "foreground", - "cursor", "accent"}); + draw_table("General UI", "##general_ui", + {"background", "on_background", "surface", "on_surface", "surface_variant", + "on_surface_variant", "foreground", "cursor", "accent"}); draw_table("Borders", "##borders", {"border_focused", "border"}); - draw_table("Semantic Colors", "##semantic", {"success", "info", "warning", "error", - "on_success", "on_info", "on_warning", "on_error"}); + draw_table( + "Semantic Colors", "##semantic", + {"success", "info", "warning", "error", "on_success", "on_info", "on_warning", "on_error"}); - draw_table("Editor", "##editor", {"editor_background", "editor_command", "editor_comment", - "editor_disabled", "editor_emphasis", "editor_error", - "editor_inactive", "editor_line_number", "editor_link", - "editor_main", "editor_selected", "editor_selection_inactive", - "editor_string", "editor_success", "editor_warning"}); + draw_table("Editor", "##editor", + {"editor_background", "editor_command", "editor_comment", "editor_disabled", + "editor_emphasis", "editor_error", "editor_inactive", "editor_line_number", + "editor_link", "editor_main", "editor_selected", "editor_selection_inactive", + "editor_string", "editor_success", "editor_warning"}); - draw_table("Terminal (Base16)", "##terminal", {"base00", "base01", "base02", "base03", - "base04", "base05", "base06", "base07", - "base08", "base09", "base0A", "base0B", - "base0C", "base0D", "base0E", "base0F"}); + draw_table("Terminal (Base16)", "##terminal", + {"base00", "base01", "base02", "base03", "base04", "base05", "base06", "base07", + "base08", "base09", "base0A", "base0B", "base0C", "base0D", "base0E", "base0F"}); } diff --git a/src/gui/views/color_table_renderer.hpp b/src/gui/views/color_table_renderer.hpp new file mode 100644 index 0000000..0aa3943 --- /dev/null +++ b/src/gui/views/color_table_renderer.hpp @@ -0,0 +1,27 @@ +#ifndef CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP +#define CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP + +#include "core/palette/palette.hpp" +#include "gui/controllers/palette_controller.hpp" +#include +#include + +class color_table_renderer +{ + public: + using OnColorChangedCallback = std::function; + + void render(const clrsync::core::palette &palette, palette_controller &controller, + const OnColorChangedCallback &on_changed); + + private: + void render_color_row(const std::string &name, const clrsync::core::palette &palette, + palette_controller &controller, const OnColorChangedCallback &on_changed); + + bool matches_filter(const std::string &name) const; + + char m_filter_text[128] = {0}; + bool m_show_only_modified{false}; +}; + +#endif // CLRSYNC_GUI_COLOR_TABLE_RENDERER_HPP diff --git a/src/gui/preview_renderer.cpp b/src/gui/views/preview_renderer.cpp similarity index 89% rename from src/gui/preview_renderer.cpp rename to src/gui/views/preview_renderer.cpp index 0228025..2c60d9c 100644 --- a/src/gui/preview_renderer.cpp +++ b/src/gui/views/preview_renderer.cpp @@ -1,6 +1,6 @@ -#include "preview_renderer.hpp" -#include "theme_applier.hpp" -#include "imgui_helpers.hpp" +#include "gui/views/preview_renderer.hpp" +#include "gui/controllers/theme_applier.hpp" +#include "gui/helpers/imgui_helpers.hpp" #include "imgui.h" #include #include @@ -74,15 +74,11 @@ int main() static ImVec4 hex_to_imvec4(uint32_t hex) { - return { - ((hex >> 24) & 0xFF) / 255.0f, - ((hex >> 16) & 0xFF) / 255.0f, - ((hex >> 8) & 0xFF) / 255.0f, - (hex & 0xFF) / 255.0f - }; + return {((hex >> 24) & 0xFF) / 255.0f, ((hex >> 16) & 0xFF) / 255.0f, + ((hex >> 8) & 0xFF) / 255.0f, (hex & 0xFF) / 255.0f}; } -void preview_renderer::apply_palette(const clrsync::core::palette& palette) +void preview_renderer::apply_palette(const clrsync::core::palette &palette) { theme_applier::apply_to_editor(m_editor, palette); } @@ -98,7 +94,7 @@ void preview_renderer::render_code_preview() m_editor.Render("##CodeEditor", ImVec2(0, code_preview_height), true); } -void preview_renderer::render_terminal_preview(const clrsync::core::palette& current) +void preview_renderer::render_terminal_preview(const clrsync::core::palette ¤t) { auto get_color = [&](const std::string &key) -> ImVec4 { const auto &col = current.get_color(key); @@ -108,7 +104,7 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur const ImVec4 fg = get_color("base07"); const ImVec4 cursor_col = get_color("cursor"); const ImVec4 border_col = get_color("border"); - + const ImVec4 black = get_color("base00"); const ImVec4 red = get_color("base01"); const ImVec4 green = get_color("base02"); @@ -117,7 +113,7 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur const ImVec4 magenta = get_color("base05"); const ImVec4 cyan = get_color("base06"); const ImVec4 white = get_color("base07"); - + const ImVec4 bright_black = get_color("base08"); const ImVec4 bright_red = get_color("base09"); const ImVec4 bright_green = get_color("base0A"); @@ -134,7 +130,7 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur ImGui::PushStyleColor(ImGuiCol_ChildBg, bg); ImGui::PushStyleColor(ImGuiCol_Border, border_col); - + const float terminal_height = std::max(200.0f, ImGui::GetContentRegionAvail().y - 10.0f); ImGui::BeginChild("TerminalPreview", ImVec2(0, terminal_height), true); @@ -154,25 +150,25 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur ImGui::TextColored(fg, " 5 user group 4096 Dec 2 10:30 "); ImGui::SameLine(0, 0); ImGui::TextColored(blue, "."); - + ImGui::TextColored(blue, "drwxr-xr-x"); ImGui::SameLine(); ImGui::TextColored(fg, " 3 user group 4096 Dec 1 09:15 "); ImGui::SameLine(0, 0); ImGui::TextColored(blue, ".."); - + ImGui::TextColored(fg, "-rw-r--r--"); ImGui::SameLine(); ImGui::TextColored(fg, " 1 user group 1234 Dec 2 10:30 "); ImGui::SameLine(0, 0); ImGui::TextColored(fg, "README.md"); - + ImGui::TextColored(fg, "-rwxr-xr-x"); ImGui::SameLine(); ImGui::TextColored(fg, " 1 user group 8192 Dec 2 10:28 "); ImGui::SameLine(0, 0); ImGui::TextColored(green, "build.sh"); - + ImGui::TextColored(cyan, "lrwxrwxrwx"); ImGui::SameLine(); ImGui::TextColored(fg, " 1 user group 24 Dec 1 15:00 "); @@ -180,7 +176,7 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur ImGui::TextColored(cyan, "config -> ~/.config/app"); ImGui::Spacing(); - + ImGui::TextColored(green, "user@host"); ImGui::SameLine(0, 0); ImGui::TextColored(fg, ":"); @@ -190,7 +186,7 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur ImGui::TextColored(fg, "$ "); ImGui::SameLine(0, 0); ImGui::TextColored(fg, "git status"); - + ImGui::TextColored(fg, "On branch "); ImGui::SameLine(0, 0); ImGui::TextColored(green, "main"); @@ -203,12 +199,12 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur ImGui::Spacing(); ImGui::TextColored(fg, "ANSI Colors (0-7 / 8-15):"); - + const float box_size = 20.0f; const float spacing = 4.0f; ImVec2 start_pos = ImGui::GetCursorScreenPos(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + std::array normal_colors = {black, red, green, yellow, blue, magenta, cyan, white}; for (size_t i = 0; i < 8; i++) { @@ -217,24 +213,26 @@ void preview_renderer::render_terminal_preview(const clrsync::core::palette& cur draw_list->AddRectFilled(p0, p1, ImGui::ColorConvertFloat4ToU32(normal_colors[i])); draw_list->AddRect(p0, p1, ImGui::ColorConvertFloat4ToU32(border_col)); } - - std::array bright_colors = {bright_black, bright_red, bright_green, bright_yellow, - bright_blue, bright_magenta, bright_cyan, bright_white}; + + std::array bright_colors = {bright_black, bright_red, bright_green, + bright_yellow, bright_blue, bright_magenta, + bright_cyan, bright_white}; for (size_t i = 0; i < 8; i++) { - ImVec2 p0 = ImVec2(start_pos.x + i * (box_size + spacing), start_pos.y + box_size + spacing); + ImVec2 p0 = + ImVec2(start_pos.x + i * (box_size + spacing), start_pos.y + box_size + spacing); ImVec2 p1 = ImVec2(p0.x + box_size, p0.y + box_size); draw_list->AddRectFilled(p0, p1, ImGui::ColorConvertFloat4ToU32(bright_colors[i])); draw_list->AddRect(p0, p1, ImGui::ColorConvertFloat4ToU32(border_col)); } - + ImGui::Dummy(ImVec2(8 * (box_size + spacing), 2 * box_size + spacing + 4)); ImGui::PopStyleColor(2); ImGui::EndChild(); } -void preview_renderer::render(const clrsync::core::palette& current) +void preview_renderer::render(const clrsync::core::palette ¤t) { if (current.colors().empty()) { diff --git a/src/gui/preview_renderer.hpp b/src/gui/views/preview_renderer.hpp similarity index 60% rename from src/gui/preview_renderer.hpp rename to src/gui/views/preview_renderer.hpp index 315ae3f..4ea0b99 100644 --- a/src/gui/preview_renderer.hpp +++ b/src/gui/views/preview_renderer.hpp @@ -1,21 +1,21 @@ #ifndef CLRSYNC_GUI_PREVIEW_RENDERER_HPP #define CLRSYNC_GUI_PREVIEW_RENDERER_HPP -#include "core/palette/palette.hpp" #include "color_text_edit/TextEditor.h" +#include "core/palette/palette.hpp" class preview_renderer { -public: + public: preview_renderer(); - - void render(const clrsync::core::palette& palette); - void apply_palette(const clrsync::core::palette& palette); -private: + void render(const clrsync::core::palette &palette); + void apply_palette(const clrsync::core::palette &palette); + + private: void render_code_preview(); - void render_terminal_preview(const clrsync::core::palette& palette); - + void render_terminal_preview(const clrsync::core::palette &palette); + TextEditor m_editor; }; diff --git a/src/gui/settings_window.cpp b/src/gui/views/settings_window.cpp similarity index 88% rename from src/gui/settings_window.cpp rename to src/gui/views/settings_window.cpp index f0e0295..7063248 100644 --- a/src/gui/settings_window.cpp +++ b/src/gui/views/settings_window.cpp @@ -1,9 +1,9 @@ -#include "settings_window.hpp" +#include "gui/views/settings_window.hpp" #include "core/config/config.hpp" #include "core/error.hpp" -#include "gui/font_loader.hpp" -#include "gui/imgui_helpers.hpp" -#include "gui/file_browser.hpp" +#include "gui/helpers/imgui_helpers.hpp" +#include "gui/platform/file_browser.hpp" +#include "gui/platform/font_loader.hpp" #include "imgui.h" #include @@ -13,10 +13,10 @@ settings_window::settings_window() m_default_theme[0] = '\0'; m_palettes_path[0] = '\0'; m_font[0] = '\0'; - + font_loader loader; m_available_fonts = loader.get_system_fonts(); - + load_settings(); } @@ -26,8 +26,9 @@ void settings_window::render() return; ImGui::SetNextWindowSize(ImVec2(700, 500), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); - + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_FirstUseEver, + ImVec2(0.5f, 0.5f)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoCollapse; if (ImGui::Begin("Settings", &m_visible, window_flags)) { @@ -38,18 +39,18 @@ void settings_window::render() render_general_tab(); ImGui::EndTabItem(); } - + if (ImGui::BeginTabItem("Appearance")) { render_appearance_tab(); ImGui::EndTabItem(); } - + ImGui::EndTabBar(); } - + render_status_messages(); - + ImGui::Separator(); render_action_buttons(); } @@ -58,20 +59,20 @@ void settings_window::render() void settings_window::load_settings() { - auto& cfg = clrsync::core::config::instance(); - + auto &cfg = clrsync::core::config::instance(); + std::string default_theme = cfg.default_theme(); strncpy(m_default_theme, default_theme.c_str(), sizeof(m_default_theme) - 1); m_default_theme[sizeof(m_default_theme) - 1] = '\0'; - + std::string palettes_path = cfg.palettes_path(); strncpy(m_palettes_path, palettes_path.c_str(), sizeof(m_palettes_path) - 1); m_palettes_path[sizeof(m_palettes_path) - 1] = '\0'; - + std::string font = cfg.font(); strncpy(m_font, font.c_str(), sizeof(m_font) - 1); m_font[sizeof(m_font) - 1] = '\0'; - + m_selected_font_idx = 0; for (int i = 0; i < static_cast(m_available_fonts.size()); i++) { @@ -81,62 +82,62 @@ void settings_window::load_settings() break; } } - + m_font_size = cfg.font_size(); - + m_error_message.clear(); m_settings_changed = false; } void settings_window::apply_settings() { - auto& cfg = clrsync::core::config::instance(); - + auto &cfg = clrsync::core::config::instance(); + if (strlen(m_default_theme) == 0) { m_error_message = "Default theme cannot be empty"; return; } - + if (strlen(m_palettes_path) == 0) { m_error_message = "Palettes path cannot be empty"; return; } - + if (strlen(m_font) == 0) { m_error_message = "Font cannot be empty"; return; } - + if (m_font_size < 8 || m_font_size > 48) { m_error_message = "Font size must be between 8 and 48"; return; } - + auto result1 = cfg.set_default_theme(m_default_theme); if (!result1) { m_error_message = "Failed to set default theme: " + result1.error().description(); return; } - + auto result2 = cfg.set_palettes_path(m_palettes_path); if (!result2) { m_error_message = "Failed to set palettes path: " + result2.error().description(); return; } - + auto result3 = cfg.set_font(m_font); if (!result3) { m_error_message = "Failed to set font: " + result3.error().description(); return; } - + auto result4 = cfg.set_font_size(m_font_size); if (!result4) { @@ -148,7 +149,7 @@ void settings_window::apply_settings() auto font = fn_loader.load_font(m_font, m_font_size); if (font) ImGui::GetIO().FontDefault = font; - + m_error_message.clear(); m_settings_changed = false; } @@ -156,25 +157,25 @@ void settings_window::apply_settings() void settings_window::render_general_tab() { ImGui::Spacing(); - + auto accent_color = palette_utils::get_color(m_current_palette, "accent"); ImGui::TextColored(accent_color, "Theme Settings"); ImGui::Separator(); ImGui::Spacing(); - + ImGui::Text("Default Theme:"); ImGui::SameLine(); show_help_marker("The default color scheme to load on startup"); ImGui::SetNextItemWidth(-100.0f); if (ImGui::InputText("##default_theme", m_default_theme, sizeof(m_default_theme))) m_settings_changed = true; - + ImGui::Spacing(); - + ImGui::TextColored(accent_color, "Path Settings"); ImGui::Separator(); ImGui::Spacing(); - + ImGui::Text("Palettes Directory:"); ImGui::SameLine(); show_help_marker("Directory where color palettes are stored\nSupports ~ for home directory"); @@ -184,8 +185,10 @@ void settings_window::render_general_tab() ImGui::SameLine(); if (ImGui::Button("Browse")) { - std::string selected_path = file_dialogs::select_folder_dialog("Select Palettes Directory", m_palettes_path); - if (!selected_path.empty()) { + std::string selected_path = + file_dialogs::select_folder_dialog("Select Palettes Directory", m_palettes_path); + if (!selected_path.empty()) + { strncpy(m_palettes_path, selected_path.c_str(), sizeof(m_palettes_path) - 1); m_palettes_path[sizeof(m_palettes_path) - 1] = '\0'; m_settings_changed = true; @@ -196,12 +199,12 @@ void settings_window::render_general_tab() void settings_window::render_appearance_tab() { ImGui::Spacing(); - + auto accent_color = palette_utils::get_color(m_current_palette, "accent"); ImGui::TextColored(accent_color, "Font Settings"); ImGui::Separator(); ImGui::Spacing(); - + ImGui::Text("Font Family:"); ImGui::SameLine(); show_help_marker("Select font family for the application interface"); @@ -223,9 +226,9 @@ void settings_window::render_appearance_tab() } ImGui::EndCombo(); } - + ImGui::Spacing(); - + ImGui::Text("Font Size:"); ImGui::SameLine(); show_help_marker("Font size for the application interface (8-48)"); @@ -236,7 +239,7 @@ void settings_window::render_appearance_tab() if (old_size != m_font_size) m_settings_changed = true; } - + ImGui::SameLine(); if (ImGui::Button("Reset")) { @@ -250,34 +253,39 @@ void settings_window::render_status_messages() if (!m_error_message.empty()) { ImGui::Spacing(); - + auto error_bg_color = palette_utils::get_color(m_current_palette, "error"); auto error_text_color = palette_utils::get_color(m_current_palette, "on_error"); - + ImGui::PushStyleColor(ImGuiCol_ChildBg, error_bg_color); ImGui::PushStyleColor(ImGuiCol_Border, error_bg_color); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 4.0f); ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f); - - if (ImGui::BeginChild("##error_box", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders)) + + if (ImGui::BeginChild("##error_box", ImVec2(0, 0), + ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders)) { ImGui::PushStyleColor(ImGuiCol_Text, error_text_color); ImGui::TextWrapped("Error: %s", m_error_message.c_str()); ImGui::PopStyleColor(); - + ImGui::Spacing(); - - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(error_bg_color.x * 0.8f, error_bg_color.y * 0.8f, error_bg_color.z * 0.8f, error_bg_color.w)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(error_bg_color.x * 0.6f, error_bg_color.y * 0.6f, error_bg_color.z * 0.6f, error_bg_color.w)); + + ImGui::PushStyleColor(ImGuiCol_Button, + ImVec4(error_bg_color.x * 0.8f, error_bg_color.y * 0.8f, + error_bg_color.z * 0.8f, error_bg_color.w)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, + ImVec4(error_bg_color.x * 0.6f, error_bg_color.y * 0.6f, + error_bg_color.z * 0.6f, error_bg_color.w)); ImGui::PushStyleColor(ImGuiCol_Text, error_text_color); - + if (ImGui::Button("Dismiss##error")) m_error_message.clear(); - + ImGui::PopStyleColor(3); } ImGui::EndChild(); - + ImGui::PopStyleVar(2); ImGui::PopStyleColor(2); } @@ -286,17 +294,17 @@ void settings_window::render_status_messages() void settings_window::render_action_buttons() { ImGui::Spacing(); - + float button_width = 100.0f; float spacing = ImGui::GetStyle().ItemSpacing.x; float window_width = ImGui::GetContentRegionAvail().x; - + float total_buttons_width = 4 * button_width + 3 * spacing; float start_pos = (window_width - total_buttons_width) * 0.5f; - + if (start_pos > 0) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + start_pos); - + if (ImGui::Button("OK", ImVec2(button_width, 0))) { apply_settings(); @@ -306,15 +314,15 @@ void settings_window::render_action_buttons() m_settings_changed = false; } } - + ImGui::SameLine(); - + bool apply_disabled = !m_settings_changed; if (apply_disabled) { ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); } - + if (ImGui::Button("Apply", ImVec2(button_width, 0)) && !apply_disabled) { apply_settings(); @@ -323,21 +331,21 @@ void settings_window::render_action_buttons() m_settings_changed = false; } } - + if (apply_disabled) { ImGui::PopStyleVar(); } - + ImGui::SameLine(); - + if (ImGui::Button("Reset", ImVec2(button_width, 0))) { reset_to_defaults(); } - + ImGui::SameLine(); - + if (ImGui::Button("Cancel", ImVec2(button_width, 0))) { load_settings(); @@ -347,7 +355,7 @@ void settings_window::render_action_buttons() } } -void settings_window::show_help_marker(const char* desc) +void settings_window::show_help_marker(const char *desc) { ImGui::TextDisabled("(?)"); if (ImGui::BeginItemTooltip()) diff --git a/src/gui/settings_window.hpp b/src/gui/views/settings_window.hpp similarity index 70% rename from src/gui/settings_window.hpp rename to src/gui/views/settings_window.hpp index 6158baf..c27f175 100644 --- a/src/gui/settings_window.hpp +++ b/src/gui/views/settings_window.hpp @@ -7,14 +7,23 @@ class settings_window { -public: + public: settings_window(); void render(); - void show() { m_visible = true; } - void hide() { m_visible = false; } - bool is_visible() const { return m_visible; } + void show() + { + m_visible = true; + } + void hide() + { + m_visible = false; + } + bool is_visible() const + { + return m_visible; + } -private: + private: void load_settings(); void save_settings(); void apply_settings(); @@ -22,28 +31,29 @@ private: void render_appearance_tab(); void render_status_messages(); void render_action_buttons(); - void show_help_marker(const char* desc); + void show_help_marker(const char *desc); void reset_to_defaults(); - -public: - void set_palette(const clrsync::core::palette& palette) { - m_current_palette = palette; + + public: + void set_palette(const clrsync::core::palette &palette) + { + m_current_palette = palette; } bool m_visible{false}; - + char m_default_theme[128]; char m_palettes_path[512]; char m_font[128]; int m_font_size; - + std::vector m_available_fonts; int m_selected_font_idx; - + std::string m_error_message; bool m_settings_changed; int m_current_tab; - + clrsync::core::palette m_current_palette; }; diff --git a/src/gui/template_editor.cpp b/src/gui/views/template_editor.cpp similarity index 89% rename from src/gui/template_editor.cpp rename to src/gui/views/template_editor.cpp index b790ab7..01b0b98 100644 --- a/src/gui/template_editor.cpp +++ b/src/gui/views/template_editor.cpp @@ -1,24 +1,21 @@ #include "template_editor.hpp" #include "core/config/config.hpp" -#include "core/theme/theme_template.hpp" #include "core/palette/color_keys.hpp" +#include "core/theme/theme_template.hpp" #include "core/utils.hpp" -#include "imgui_helpers.hpp" -#include "file_browser.hpp" +#include "gui/helpers/imgui_helpers.hpp" +#include "gui/platform/file_browser.hpp" #include "imgui.h" #include #include #include #include -namespace { - const std::vector COLOR_FORMATS = { - "hex", "hex_stripped", "hexa", "hexa_stripped", - "r", "g", "b", "a", - "rgb", "rgba", - "h", "s", "l", - "hsl", "hsla" - }; +namespace +{ +const std::vector COLOR_FORMATS = { + "hex", "hex_stripped", "hexa", "hexa_stripped", "r", "g", "b", "a", "rgb", "rgba", "h", "s", + "l", "hsl", "hsla"}; } template_editor::template_editor() : m_template_name("new_template") @@ -29,7 +26,7 @@ template_editor::template_editor() : m_template_name("new_template") m_autocomplete_text_color = ImVec4(0.85f, 0.85f, 0.9f, 1.0f); m_autocomplete_selected_text_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); m_autocomplete_dim_text_color = ImVec4(0.6f, 0.6f, 0.7f, 1.0f); - + TextEditor::LanguageDefinition lang; lang.mName = "Template"; @@ -101,24 +98,25 @@ void template_editor::apply_current_palette(const clrsync::core::palette &pal) get_color_u32("border_focused", "border"); m_editor.SetPalette(palette); - + m_autocomplete_bg_color = palette_utils::get_color(pal, "surface", "background"); m_autocomplete_bg_color.w = 0.98f; m_autocomplete_border_color = palette_utils::get_color(pal, "border", "surface_variant"); m_autocomplete_selected_color = palette_utils::get_color(pal, "accent", "surface_variant"); m_autocomplete_text_color = palette_utils::get_color(pal, "on_surface", "foreground"); m_autocomplete_selected_text_color = palette_utils::get_color(pal, "on_surface", "foreground"); - m_autocomplete_dim_text_color = palette_utils::get_color(pal, "on_surface_variant", "editor_inactive"); + m_autocomplete_dim_text_color = + palette_utils::get_color(pal, "on_surface_variant", "editor_inactive"); } void template_editor::update_autocomplete_suggestions() { m_autocomplete_suggestions.clear(); - + auto cursor = m_editor.GetCursorPosition(); std::string line = m_editor.GetCurrentLineText(); int col = cursor.mColumn; - + // Check if inside '{' int brace_pos = -1; for (int i = col - 1; i >= 0; --i) @@ -136,25 +134,24 @@ void template_editor::update_autocomplete_suggestions() } } } - + if (brace_pos < 0) { m_show_autocomplete = false; - m_autocomplete_dismissed = false; + m_autocomplete_dismissed = false; return; } - + if (m_autocomplete_dismissed) { bool should_reset_dismissal = false; - - if (cursor.mLine != m_dismiss_position.mLine || - brace_pos != m_dismiss_brace_pos || + + if (cursor.mLine != m_dismiss_position.mLine || brace_pos != m_dismiss_brace_pos || abs(cursor.mColumn - m_dismiss_position.mColumn) > 3) { should_reset_dismissal = true; } - + if (should_reset_dismissal) { m_autocomplete_dismissed = false; @@ -165,17 +162,17 @@ void template_editor::update_autocomplete_suggestions() return; } } - + m_autocomplete_prefix = line.substr(brace_pos + 1, col - brace_pos - 1); m_autocomplete_start_pos = TextEditor::Coordinates(cursor.mLine, brace_pos + 1); - + size_t dot_pos = m_autocomplete_prefix.find('.'); - + if (dot_pos != std::string::npos) { std::string color_key = m_autocomplete_prefix.substr(0, dot_pos); std::string format_prefix = m_autocomplete_prefix.substr(dot_pos + 1); - + bool valid_key = false; for (size_t i = 0; i < clrsync::core::NUM_COLOR_KEYS; ++i) { @@ -185,13 +182,12 @@ void template_editor::update_autocomplete_suggestions() break; } } - + if (valid_key) { for (const auto &fmt : COLOR_FORMATS) { - if (format_prefix.empty() || - fmt.find(format_prefix) == 0 || + if (format_prefix.empty() || fmt.find(format_prefix) == 0 || fmt.find(format_prefix) != std::string::npos) { m_autocomplete_suggestions.push_back(color_key + "." + fmt); @@ -204,24 +200,23 @@ void template_editor::update_autocomplete_suggestions() for (size_t i = 0; i < clrsync::core::NUM_COLOR_KEYS; ++i) { std::string key = clrsync::core::COLOR_KEYS[i]; - if (m_autocomplete_prefix.empty() || - key.find(m_autocomplete_prefix) == 0 || + if (m_autocomplete_prefix.empty() || key.find(m_autocomplete_prefix) == 0 || key.find(m_autocomplete_prefix) != std::string::npos) { m_autocomplete_suggestions.push_back(key); } } } - + std::sort(m_autocomplete_suggestions.begin(), m_autocomplete_suggestions.end(), - [this](const std::string &a, const std::string &b) { - bool a_prefix = a.find(m_autocomplete_prefix) == 0; - bool b_prefix = b.find(m_autocomplete_prefix) == 0; - if (a_prefix != b_prefix) - return a_prefix; - return a < b; - }); - + [this](const std::string &a, const std::string &b) { + bool a_prefix = a.find(m_autocomplete_prefix) == 0; + bool b_prefix = b.find(m_autocomplete_prefix) == 0; + if (a_prefix != b_prefix) + return a_prefix; + return a < b; + }); + m_show_autocomplete = !m_autocomplete_suggestions.empty(); if (m_show_autocomplete && m_autocomplete_selected >= (int)m_autocomplete_suggestions.size()) { @@ -229,34 +224,36 @@ void template_editor::update_autocomplete_suggestions() } } -void template_editor::render_autocomplete(const ImVec2& editor_pos) +void template_editor::render_autocomplete(const ImVec2 &editor_pos) { if (!m_show_autocomplete || m_autocomplete_suggestions.empty()) return; - + float line_height = ImGui::GetTextLineHeightWithSpacing(); float char_width = ImGui::GetFontSize() * 0.5f; auto cursor = m_editor.GetCursorPosition(); - + const float line_number_width = 50.0f; - + ImVec2 popup_pos; - popup_pos.x = editor_pos.x + line_number_width + (m_autocomplete_start_pos.mColumn * char_width); + popup_pos.x = + editor_pos.x + line_number_width + (m_autocomplete_start_pos.mColumn * char_width); popup_pos.y = editor_pos.y + ((cursor.mLine + 1) * line_height); - + ImGui::SetNextWindowPos(popup_pos, ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(300, 0)); - + ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize; - + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_AlwaysAutoResize; + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 6)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 2)); ImGui::PushStyleColor(ImGuiCol_WindowBg, m_autocomplete_bg_color); ImGui::PushStyleColor(ImGuiCol_Border, m_autocomplete_border_color); - + if (ImGui::Begin("##autocomplete", nullptr, flags)) { ImGui::PushStyleColor(ImGuiCol_Text, m_autocomplete_dim_text_color); @@ -266,14 +263,14 @@ void template_editor::render_autocomplete(const ImVec2& editor_pos) ImGui::Text("Color Keys"); ImGui::PopStyleColor(); ImGui::Separator(); - + int max_items = std::min((int)m_autocomplete_suggestions.size(), 8); - + for (int i = 0; i < max_items; ++i) { const auto &suggestion = m_autocomplete_suggestions[i]; bool is_selected = (i == m_autocomplete_selected); - + if (is_selected) { ImVec4 selected_hover = m_autocomplete_selected_color; @@ -292,11 +289,11 @@ void template_editor::render_autocomplete(const ImVec2& editor_pos) ImGui::PushStyleColor(ImGuiCol_HeaderHovered, hover_bg); ImGui::PushStyleColor(ImGuiCol_Text, m_autocomplete_text_color); } - + std::string display_text = " " + suggestion; - - if (ImGui::Selectable(display_text.c_str(), is_selected, - ImGuiSelectableFlags_None, ImVec2(0, 0))) + + if (ImGui::Selectable(display_text.c_str(), is_selected, ImGuiSelectableFlags_None, + ImVec2(0, 0))) { auto start = m_autocomplete_start_pos; auto end = m_editor.GetCursorPosition(); @@ -306,15 +303,15 @@ void template_editor::render_autocomplete(const ImVec2& editor_pos) m_show_autocomplete = false; m_autocomplete_dismissed = false; } - + ImGui::PopStyleColor(3); - + if (is_selected && ImGui::IsWindowAppearing()) { ImGui::SetScrollHereY(); } } - + if (m_autocomplete_suggestions.size() > 8) { ImGui::Separator(); @@ -322,14 +319,14 @@ void template_editor::render_autocomplete(const ImVec2& editor_pos) ImGui::Text(" +%d more", (int)m_autocomplete_suggestions.size() - 8); ImGui::PopStyleColor(); } - + ImGui::Separator(); ImGui::PushStyleColor(ImGuiCol_Text, m_autocomplete_dim_text_color); ImGui::Text(" Tab/Enter: accept | Esc: dismiss"); ImGui::PopStyleColor(); } ImGui::End(); - + ImGui::PopStyleColor(2); ImGui::PopStyleVar(3); } @@ -362,8 +359,7 @@ void template_editor::render() } palette_utils::render_delete_confirmation_popup( - "Delete Template?", m_template_name, "template", m_current_palette, - [this]() { + "Delete Template?", m_template_name, "template", m_current_palette, [this]() { bool success = m_template_controller.remove_template(m_template_name); if (success) { @@ -382,7 +378,7 @@ void template_editor::render() void template_editor::render_controls() { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 8)); - + if (ImGui::Button(" + New ")) { new_template(); @@ -408,10 +404,8 @@ void template_editor::render_controls() { ImGui::SameLine(); auto error = palette_utils::get_color(m_current_palette, "error"); - auto error_hover = ImVec4(error.x * 1.1f, error.y * 1.1f, error.z * 1.1f, - error.w); - auto error_active = ImVec4(error.x * 0.8f, error.y * 0.8f, error.z * 0.8f, - error.w); + auto error_hover = ImVec4(error.x * 1.1f, error.y * 1.1f, error.z * 1.1f, error.w); + auto error_active = ImVec4(error.x * 0.8f, error.y * 0.8f, error.z * 0.8f, error.w); auto on_error = palette_utils::get_color(m_current_palette, "on_error"); ImGui::PushStyleColor(ImGuiCol_Button, error); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_hover); @@ -428,41 +422,47 @@ void template_editor::render_controls() ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 10); - + bool enabled_changed = false; if (m_enabled) { ImVec4 success_color = palette_utils::get_color(m_current_palette, "success", "accent"); - ImVec4 success_on_color = palette_utils::get_color(m_current_palette, "on_success", "on_surface"); - ImVec4 success_hover = ImVec4(success_color.x * 1.2f, success_color.y * 1.2f, success_color.z * 1.2f, 0.6f); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(success_color.x, success_color.y, success_color.z, 0.5f)); + ImVec4 success_on_color = + palette_utils::get_color(m_current_palette, "on_success", "on_surface"); + ImVec4 success_hover = + ImVec4(success_color.x * 1.2f, success_color.y * 1.2f, success_color.z * 1.2f, 0.6f); + ImGui::PushStyleColor(ImGuiCol_FrameBg, + ImVec4(success_color.x, success_color.y, success_color.z, 0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, success_hover); ImGui::PushStyleColor(ImGuiCol_CheckMark, success_on_color); } else { ImVec4 error_color = palette_utils::get_color(m_current_palette, "error", "accent"); - ImVec4 error_on_color = palette_utils::get_color(m_current_palette, "on_error", "on_surface"); - ImVec4 error_hover = ImVec4(error_color.x * 1.2f, error_color.y * 1.2f, error_color.z * 1.2f, 0.6f); - ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(error_color.x, error_color.y, error_color.z, 0.5f)); + ImVec4 error_on_color = + palette_utils::get_color(m_current_palette, "on_error", "on_surface"); + ImVec4 error_hover = + ImVec4(error_color.x * 1.2f, error_color.y * 1.2f, error_color.z * 1.2f, 0.6f); + ImGui::PushStyleColor(ImGuiCol_FrameBg, + ImVec4(error_color.x, error_color.y, error_color.z, 0.5f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, error_hover); ImGui::PushStyleColor(ImGuiCol_CheckMark, error_on_color); } - + enabled_changed = ImGui::Checkbox("Enabled", &m_enabled); ImGui::PopStyleColor(3); - + if (enabled_changed && m_is_editing_existing) { m_template_controller.set_template_enabled(m_template_name, m_enabled); } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Enable/disable this template for theme application"); - + ImGui::PopStyleVar(); - + ImGui::Spacing(); - + ImGui::AlignTextToFramePadding(); ImGui::Text("Name:"); ImGui::SameLine(80); @@ -486,8 +486,8 @@ void template_editor::render_controls() ImGui::SetNextItemWidth(-120.0f); char input_path_buf[512] = {0}; snprintf(input_path_buf, sizeof(input_path_buf), "%s", m_input_path.c_str()); - if (ImGui::InputTextWithHint("##input_path", "Path to template file...", - input_path_buf, sizeof(input_path_buf))) + if (ImGui::InputTextWithHint("##input_path", "Path to template file...", input_path_buf, + sizeof(input_path_buf))) { m_input_path = input_path_buf; if (!m_input_path.empty()) @@ -502,10 +502,13 @@ void template_editor::render_controls() ImGui::SameLine(); if (ImGui::Button("Browse##input")) { - std::string selected_path = file_dialogs::open_file_dialog("Select Template File", m_input_path); - if (!selected_path.empty()) { + std::string selected_path = + file_dialogs::open_file_dialog("Select Template File", m_input_path); + if (!selected_path.empty()) + { m_input_path = selected_path; - if (m_is_editing_existing) { + if (m_is_editing_existing) + { m_template_controller.set_template_input_path(m_template_name, m_input_path); } m_validation_error = ""; @@ -520,8 +523,8 @@ void template_editor::render_controls() ImGui::SetNextItemWidth(-120.0f); char path_buf[512] = {0}; snprintf(path_buf, sizeof(path_buf), "%s", m_output_path.c_str()); - if (ImGui::InputTextWithHint("##output_path", "Path for generated config...", - path_buf, sizeof(path_buf))) + if (ImGui::InputTextWithHint("##output_path", "Path for generated config...", path_buf, + sizeof(path_buf))) { m_output_path = path_buf; if (!m_output_path.empty()) @@ -536,10 +539,13 @@ void template_editor::render_controls() ImGui::SameLine(); if (ImGui::Button("Browse##output")) { - std::string selected_path = file_dialogs::save_file_dialog("Select Output File", m_output_path); - if (!selected_path.empty()) { + std::string selected_path = + file_dialogs::save_file_dialog("Select Output File", m_output_path); + if (!selected_path.empty()) + { m_output_path = selected_path; - if (m_is_editing_existing) { + if (m_is_editing_existing) + { m_template_controller.set_template_output_path(m_template_name, m_output_path); } m_validation_error = ""; @@ -554,8 +560,8 @@ void template_editor::render_controls() ImGui::SetNextItemWidth(-FLT_MIN); char reload_buf[512] = {0}; snprintf(reload_buf, sizeof(reload_buf), "%s", m_reload_command.c_str()); - if (ImGui::InputTextWithHint("##reload_cmd", "Command to reload app (optional)...", - reload_buf, sizeof(reload_buf))) + if (ImGui::InputTextWithHint("##reload_cmd", "Command to reload app (optional)...", reload_buf, + sizeof(reload_buf))) { m_reload_command = reload_buf; if (m_is_editing_existing) @@ -579,7 +585,7 @@ void template_editor::render_controls() void template_editor::render_editor() { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 4)); - + if (!m_is_editing_existing) { ImVec4 success_color = palette_utils::get_color(m_current_palette, "success", "accent"); @@ -608,19 +614,19 @@ void template_editor::render_editor() ImGui::PopStyleColor(); } } - + ImGui::PopStyleVar(); ImGui::Separator(); bool consume_keys = false; - + if (m_show_autocomplete && ImGui::IsKeyPressed(ImGuiKey_Escape, false)) { m_show_autocomplete = false; m_autocomplete_dismissed = true; - + m_dismiss_position = m_editor.GetCursorPosition(); - + std::string line = m_editor.GetCurrentLineText(); m_dismiss_brace_pos = -1; for (int i = m_dismiss_position.mColumn - 1; i >= 0; --i) @@ -631,13 +637,13 @@ void template_editor::render_editor() break; } } - + consume_keys = true; } else if (m_show_autocomplete && !m_autocomplete_suggestions.empty()) { int max_visible = std::min((int)m_autocomplete_suggestions.size(), 8); - + if (ImGui::IsKeyPressed(ImGuiKey_DownArrow, false)) { m_autocomplete_selected = (m_autocomplete_selected + 1) % max_visible; @@ -648,7 +654,7 @@ void template_editor::render_editor() m_autocomplete_selected = (m_autocomplete_selected - 1 + max_visible) % max_visible; consume_keys = true; } - else if (ImGui::IsKeyPressed(ImGuiKey_Tab, false) || + else if (ImGui::IsKeyPressed(ImGuiKey_Tab, false) || ImGui::IsKeyPressed(ImGuiKey_Enter, false)) { auto start = m_autocomplete_start_pos; @@ -657,25 +663,25 @@ void template_editor::render_editor() m_editor.Delete(); m_editor.InsertText(m_autocomplete_suggestions[m_autocomplete_selected] + "}"); m_show_autocomplete = false; - m_autocomplete_dismissed = false; + m_autocomplete_dismissed = false; consume_keys = true; } } - + if (consume_keys) { m_editor.SetHandleKeyboardInputs(false); } ImVec2 editor_pos = ImGui::GetCursorScreenPos(); - + m_editor.Render("##TemplateEditor", ImVec2(0, 0), true); - + if (consume_keys) { m_editor.SetHandleKeyboardInputs(true); } - + update_autocomplete_suggestions(); render_autocomplete(editor_pos); } @@ -683,7 +689,7 @@ void template_editor::render_editor() void template_editor::render_template_list() { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 6)); - + ImGui::Text("Templates"); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 20); ImGui::TextDisabled("(%d)", (int)m_template_controller.templates().size()); @@ -705,23 +711,24 @@ void template_editor::render_template_list() for (const auto &[key, tmpl] : templates) { const bool selected = (m_template_name == key && m_is_editing_existing); - + if (!tmpl.enabled()) { - ImVec4 disabled_color = palette_utils::get_color(m_current_palette, "on_surface_variant", "editor_inactive"); + ImVec4 disabled_color = palette_utils::get_color( + m_current_palette, "on_surface_variant", "editor_inactive"); ImGui::PushStyleColor(ImGuiCol_Text, disabled_color); } - + if (ImGui::Selectable(key.c_str(), selected)) { load_template(key); } - + if (!tmpl.enabled()) { ImGui::PopStyleColor(); } - + if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); @@ -733,7 +740,7 @@ void template_editor::render_template_list() ImGui::EndTooltip(); } } - + ImGui::PopStyleVar(); } @@ -830,7 +837,7 @@ void template_editor::save_template() auto &cfg = clrsync::core::config::instance(); std::filesystem::path template_file = clrsync::core::normalize_path(trimmed_input_path); - + auto parent_dir = template_file.parent_path(); if (!parent_dir.empty() && !std::filesystem::exists(parent_dir)) { @@ -838,7 +845,7 @@ void template_editor::save_template() { std::filesystem::create_directories(parent_dir); } - catch (const std::exception& e) + catch (const std::exception &e) { m_validation_error = "Error: Could not create directory for input path"; return; @@ -853,7 +860,7 @@ void template_editor::save_template() m_validation_error = "Failed to write template file"; return; } - + out << template_content; out.close(); @@ -937,7 +944,7 @@ void template_editor::delete_template() { if (!m_is_editing_existing || m_template_name.empty()) return; - + m_show_delete_confirmation = true; } diff --git a/src/gui/template_editor.hpp b/src/gui/views/template_editor.hpp similarity index 89% rename from src/gui/template_editor.hpp rename to src/gui/views/template_editor.hpp index 38c1ab1..b8dd682 100644 --- a/src/gui/template_editor.hpp +++ b/src/gui/views/template_editor.hpp @@ -1,25 +1,25 @@ #ifndef CLRSYNC_GUI_TEMPLATE_EDITOR_HPP #define CLRSYNC_GUI_TEMPLATE_EDITOR_HPP -#include "template_controller.hpp" -#include #include "color_text_edit/TextEditor.h" +#include "gui/controllers/template_controller.hpp" #include "imgui.h" +#include #include #include class template_editor { -public: + public: template_editor(); void render(); - void apply_current_palette(const clrsync::core::palette& pal); + void apply_current_palette(const clrsync::core::palette &pal); -private: + private: void render_controls(); void render_editor(); void render_template_list(); - void render_autocomplete(const ImVec2& editor_pos); + void render_autocomplete(const ImVec2 &editor_pos); void update_autocomplete_suggestions(); void save_template(); @@ -32,7 +32,7 @@ private: template_controller m_template_controller; TextEditor m_editor; - + std::string m_template_name; std::string m_input_path; std::string m_output_path; @@ -40,7 +40,7 @@ private: std::string m_validation_error; std::string m_saved_content; bool m_has_unsaved_changes = false; - + bool m_enabled{true}; bool m_is_editing_existing{false}; bool m_show_delete_confirmation{false}; @@ -53,14 +53,14 @@ private: int m_autocomplete_selected{0}; std::string m_autocomplete_prefix; TextEditor::Coordinates m_autocomplete_start_pos; - + ImVec4 m_autocomplete_bg_color; ImVec4 m_autocomplete_border_color; ImVec4 m_autocomplete_selected_color; ImVec4 m_autocomplete_text_color; ImVec4 m_autocomplete_selected_text_color; ImVec4 m_autocomplete_dim_text_color; - + clrsync::core::palette m_current_palette; };