diff --git a/src/core/config/config.cpp b/src/core/config/config.cpp index 3578175..0743bb7 100644 --- a/src/core/config/config.cpp +++ b/src/core/config/config.cpp @@ -216,6 +216,32 @@ Result config::update_template(const std::string &key, return m_file->save_file(); } +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 { + std::filesystem::remove(template_file); + } 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(); +} + const std::unordered_map config::templates() { if (m_themes.empty() && m_file) diff --git a/src/core/config/config.hpp b/src/core/config/config.hpp index a53d16f..cab4b55 100644 --- a/src/core/config/config.hpp +++ b/src/core/config/config.hpp @@ -33,6 +33,7 @@ class config Result update_template(const std::string &key, const clrsync::core::theme_template &theme_template); + Result remove_template(const std::string &key); static std::filesystem::path get_data_dir(); private: diff --git a/src/core/io/file.hpp b/src/core/io/file.hpp index 96d910c..0880623 100644 --- a/src/core/io/file.hpp +++ b/src/core/io/file.hpp @@ -41,6 +41,7 @@ class file } 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(); }; }; } // namespace clrsync::core::io diff --git a/src/core/io/toml_file.cpp b/src/core/io/toml_file.cpp index 5c1afe0..fa41bc3 100644 --- a/src/core/io/toml_file.cpp +++ b/src/core/io/toml_file.cpp @@ -92,6 +92,25 @@ void toml_file::insert_or_update_value(const std::string §ion, const std::st std::visit([&](auto &&v) { tbl->insert_or_assign(key, v); }, value); } +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(); + if (!sub) + return; + tbl = sub; + } + + tbl->erase(parts.back()); +} + Result toml_file::save_file() { try { diff --git a/src/core/io/toml_file.hpp b/src/core/io/toml_file.hpp index a2f1c65..939ee7f 100644 --- a/src/core/io/toml_file.hpp +++ b/src/core/io/toml_file.hpp @@ -19,6 +19,7 @@ class toml_file : public file std::map get_table(const std::string §ion_path) const override; void insert_or_update_value(const std::string §ion, const std::string &key, const value_type &value) override; + void remove_section(const std::string §ion) override; Result save_file() override; private: diff --git a/src/gui/color_scheme_editor.cpp b/src/gui/color_scheme_editor.cpp index 1484adb..7d8ada6 100644 --- a/src/gui/color_scheme_editor.cpp +++ b/src/gui/color_scheme_editor.cpp @@ -133,10 +133,14 @@ void color_scheme_editor::render_controls() } ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.3f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.6f, 0.1f, 0.1f, 1.0f)); if (ImGui::Button("Delete")) { m_controller.delete_current_palette(); } + ImGui::PopStyleColor(3); ImGui::SameLine(); if (ImGui::Button("Apply")) diff --git a/src/gui/template_controller.cpp b/src/gui/template_controller.cpp index 061196f..68a6f8b 100644 --- a/src/gui/template_controller.cpp +++ b/src/gui/template_controller.cpp @@ -15,6 +15,15 @@ void template_controller::set_template_enabled(const std::string& key, bool enab } } +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()) { + 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) { auto it = m_templates.find(key); @@ -33,6 +42,16 @@ void template_controller::set_template_reload_command(const std::string& key, co } } +bool template_controller::remove_template(const std::string& key) +{ + auto result = clrsync::core::config::instance().remove_template(key); + if (result) { + m_templates.erase(key); + return true; + } + return false; +} + void template_controller::refresh() { m_templates = m_template_manager.templates(); diff --git a/src/gui/template_controller.hpp b/src/gui/template_controller.hpp index c45fa91..e01484f 100644 --- a/src/gui/template_controller.hpp +++ b/src/gui/template_controller.hpp @@ -12,8 +12,10 @@ 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); void refresh(); private: diff --git a/src/gui/template_editor.cpp b/src/gui/template_editor.cpp index 150ca3b..80d67a8 100644 --- a/src/gui/template_editor.cpp +++ b/src/gui/template_editor.cpp @@ -118,6 +118,40 @@ void template_editor::render() render_editor(); ImGui::EndChild(); + if (m_show_delete_confirmation) + { + ImGui::OpenPopup("Delete Template?"); + m_show_delete_confirmation = false; + } + + if (ImGui::BeginPopupModal("Delete Template?", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Are you sure you want to delete '%s'?", m_template_name.c_str()); + ImGui::Text("This action cannot be undone."); + ImGui::Separator(); + + if (ImGui::Button("Delete", ImVec2(120, 0))) + { + bool success = m_template_controller.remove_template(m_template_name); + if (success) + { + new_template(); + refresh_templates(); + } + else + { + m_validation_error = "Failed to delete template"; + } + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120, 0))) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::End(); } @@ -140,6 +174,19 @@ void template_editor::render_controls() save_template(); } + if (m_is_editing_existing) + { + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.3f, 0.3f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.6f, 0.1f, 0.1f, 1.0f)); + if (ImGui::Button("Delete")) + { + delete_template(); + } + ImGui::PopStyleColor(3); + } + ImGui::SameLine(); ImGui::Text("Template Name:"); ImGui::SameLine(); @@ -164,6 +211,24 @@ void template_editor::render_controls() } } + ImGui::Text("Input Path:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(-FLT_MIN); + char input_path_buf[512] = {0}; + snprintf(input_path_buf, sizeof(input_path_buf), "%s", m_input_path.c_str()); + if (ImGui::InputText("##input_path", input_path_buf, sizeof(input_path_buf))) + { + m_input_path = input_path_buf; + if (!m_input_path.empty()) + { + m_validation_error = ""; + } + if (m_is_editing_existing) + { + m_template_controller.set_template_input_path(m_template_name, m_input_path); + } + } + ImGui::Text("Output Path:"); ImGui::SameLine(); ImGui::SetNextItemWidth(-FLT_MIN); @@ -318,6 +383,10 @@ void template_editor::save_template() trimmed_name.erase(0, trimmed_name.find_first_not_of(" \t\n\r")); trimmed_name.erase(trimmed_name.find_last_not_of(" \t\n\r") + 1); + std::string trimmed_input_path = m_input_path; + trimmed_input_path.erase(0, trimmed_input_path.find_first_not_of(" \t\n\r")); + trimmed_input_path.erase(trimmed_input_path.find_last_not_of(" \t\n\r") + 1); + std::string trimmed_path = m_output_path; trimmed_path.erase(0, trimmed_path.find_first_not_of(" \t\n\r")); trimmed_path.erase(trimmed_path.find_last_not_of(" \t\n\r") + 1); @@ -328,6 +397,12 @@ void template_editor::save_template() return; } + if (trimmed_input_path.empty()) + { + m_validation_error = "Error: Input path cannot be empty!"; + return; + } + if (trimmed_path.empty()) { m_validation_error = "Error: Output path cannot be empty!"; @@ -344,29 +419,22 @@ void template_editor::save_template() m_validation_error = ""; auto &cfg = clrsync::core::config::instance(); - std::string palettes_path = cfg.palettes_path(); - std::filesystem::path templates_dir = - std::filesystem::path(palettes_path).parent_path() / "templates"; - if (!std::filesystem::exists(templates_dir)) + std::filesystem::path template_file = trimmed_input_path; + + // Ensure the parent directory exists + auto parent_dir = template_file.parent_path(); + if (!parent_dir.empty() && !std::filesystem::exists(parent_dir)) { - std::filesystem::create_directories(templates_dir); - } - - std::filesystem::path template_file; - if (m_is_editing_existing) - { - auto existing_template_result = cfg.template_by_name(trimmed_name); - if (!existing_template_result) + try { - m_validation_error = "Template not found: " + existing_template_result.error().description(); + std::filesystem::create_directories(parent_dir); + } + catch (const std::exception& e) + { + m_validation_error = "Error: Could not create directory for input path"; return; } - template_file = existing_template_result.value()->template_path(); - } - else - { - template_file = templates_dir / trimmed_name; } std::string template_content = m_editor.GetText(); @@ -393,6 +461,7 @@ void template_editor::save_template() } m_template_name = trimmed_name; + m_input_path = trimmed_input_path; m_output_path = trimmed_path; m_is_editing_existing = true; m_saved_content = m_editor.GetText(); @@ -410,6 +479,7 @@ void template_editor::load_template(const std::string &name) { const auto &tmpl = it->second; m_template_name = name; + m_input_path = tmpl.template_path(); m_output_path = tmpl.output_path(); m_reload_command = tmpl.reload_command(); m_enabled = tmpl.enabled(); @@ -446,6 +516,7 @@ void template_editor::new_template() "Examples: {color.hex}, {color.rgb}, {color.r}\n\n"; m_editor.SetText(default_content); m_saved_content = default_content; + m_input_path = ""; m_output_path = ""; m_reload_command = ""; m_enabled = true; @@ -454,6 +525,14 @@ void template_editor::new_template() m_has_unsaved_changes = false; } +void template_editor::delete_template() +{ + if (!m_is_editing_existing || m_template_name.empty()) + return; + + m_show_delete_confirmation = true; +} + void template_editor::refresh_templates() { m_template_controller.refresh(); diff --git a/src/gui/template_editor.hpp b/src/gui/template_editor.hpp index 8fe77c3..eca7f13 100644 --- a/src/gui/template_editor.hpp +++ b/src/gui/template_editor.hpp @@ -21,6 +21,7 @@ private: void save_template(); void load_template(const std::string &name); void new_template(); + void delete_template(); void refresh_templates(); bool is_valid_path(const std::string &path); @@ -29,6 +30,7 @@ private: TextEditor m_editor; std::string m_template_name; + std::string m_input_path; std::string m_output_path; std::string m_reload_command; std::string m_validation_error; @@ -37,6 +39,7 @@ private: bool m_enabled{true}; bool m_is_editing_existing{false}; + bool m_show_delete_confirmation{false}; }; #endif // CLRSYNC_GUI_TEMPLATE_EDITOR_HPP \ No newline at end of file