#include "pch.h" #include "log.h" #include "node_panel_brush.h" #include "legacy_brush_preset_panel_ui.h" #include "legacy_brush_panel_services.h" #include "app_core/brush_ui.h" #include "legacy_brush_ui_services.h" #include "legacy_brush_preset_services.h" #include "legacy_ui_overlay_services.h" #include "asset.h" #include "texture.h" #ifdef __APPLE__ #include #endif #include "canvas.h" #include "app.h" #include "abr.h" Node* NodeButtonBrush::clone_instantiate() const { return new NodeButtonBrush(); } void NodeButtonBrush::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-brush-icon"); color_hover = glm::vec4(.7, .7, .7, 1); color_normal = glm::vec4(.3, .3, .3, 1); m_color = color_normal; img = (NodeImage*)m_children[0].get(); } void NodeButtonBrush::set_icon(const char* path) { img->m_path = path; img->m_tex_id = const_hash(img->m_path.c_str()); img->m_use_mipmaps = true; img->create(); } void NodeButtonBrush::draw() { m_color = m_mouse_inside ? color_hover : color_normal; m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color; NodeButtonCustom::draw(); } bool NodeButtonBrush::read(BinaryStreamReader& r) { Serializer::Descriptor d; r >> d; d.value("brush_name", brush_name); d.value("high_path", high_path); d.value("thumb_path", thumb_path); d.value("m_user_brush", m_user_brush); high_path = str_replace(high_path, "{data_path}", App::I->data_path); thumb_path = str_replace(thumb_path, "{data_path}", App::I->data_path); return true; } void NodeButtonBrush::write(BinaryStreamWriter& w) const { Serializer::Descriptor d; d.class_id = "brush"; d.name = L"Brush class"; d.props["brush_name"] = std::make_shared(brush_name); d.props["high_path"] = std::make_shared( str_replace(high_path, App::I->data_path, "{data_path}")); d.props["thumb_path"] = std::make_shared( str_replace(thumb_path, App::I->data_path, "{data_path}")); d.props["m_user_brush"] = std::make_shared(m_user_brush); w << d; } Node* NodePanelBrush::clone_instantiate() const { return new NodePanelBrush(); } void NodePanelBrush::execute_texture_list_plan(const pp::app::BrushTextureListPlan& plan) { const auto status = pp::panopainter::execute_legacy_brush_texture_list_plan(*this, plan); if (!status.ok()) { LOG("Brush texture list action failed: %s", status.message); } } void NodePanelBrush::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-panel-brushes"); m_btn_add = find("btn-add"); m_btn_add->on_click = [this](Node*) { App::I->pick_file({ "JPG", "PNG" }, [this](std::string path) { const auto plan = pp::app::plan_brush_texture_list_add(m_dir_name, App::I->data_path, path); if (plan) { execute_texture_list_plan(plan.value()); } }); }; m_btn_remove = find("btn-remove"); m_btn_remove->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); const auto plan = pp::app::plan_brush_texture_list_remove( static_cast(m_container->m_children.size()), idx, m_current->m_user_brush); if (plan) { execute_texture_list_plan(plan.value()); } } }; m_btn_up = find("btn-up"); m_btn_up->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); const auto plan = pp::app::plan_brush_texture_list_move( static_cast(m_container->m_children.size()), idx, -1); if (plan) { execute_texture_list_plan(plan.value()); } } }; m_btn_down = find("btn-down"); m_btn_down->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); const auto plan = pp::app::plan_brush_texture_list_move( static_cast(m_container->m_children.size()), idx, 1); if (plan) { execute_texture_list_plan(plan.value()); } } }; m_container = find("brushes"); if (Asset::exist(App::I->data_path + "/settings/" + m_dir_name + ".bin") && !restore()) { auto mb = App::I->message_box("Brushes", "Could not read brush textures file, it will be deleted.", true); mb->btn_ok->on_click = [this, mb](Node*) { Asset::delete_file(App::I->data_path + "/settings/" + m_dir_name + ".bin"); pp::panopainter::close_legacy_dialog_node(*mb); }; mb->btn_cancel->on_click = [mb](Node*) { pp::panopainter::close_legacy_dialog_node(*mb); }; } if (m_container->m_children.empty() && !m_dir_name.empty()) scan(); save(); } kEventResult NodePanelBrush::handle_event(Event* e) { switch (e->m_type) { case kEventType::MouseLeave: if (!m_interacted) break; // else fall through case kEventType::MouseUpL: if (!m_mouse_inside) { pp::panopainter::release_legacy_mouse_capture(*this); if (m_parent) { pp::panopainter::detach_legacy_node_from_parent(*this); } if (on_popup_close) { on_popup_close(this); } } break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } void NodePanelBrush::handle_click(Node* target) { if (target == m_current) return; if (m_current) m_current->m_selected = false; m_current = (NodeButtonBrush*)target; m_current->m_selected = true; if (on_brush_changed) on_brush_changed(this, m_container->get_child_index(target)); m_interacted = true; } int NodePanelBrush::find_brush(const std::string & name) const { return pp::panopainter::LegacyBrushPanelServices(const_cast(*this)).find_brush(name); } std::string NodePanelBrush::get_texture_path(int index) const { return pp::panopainter::LegacyBrushPanelServices(const_cast(*this)).get_texture_path(index); } std::string NodePanelBrush::get_thumb_path(int index) const { return pp::panopainter::LegacyBrushPanelServices(const_cast(*this)).get_thumb_path(index); } bool NodePanelBrush::save() { return pp::panopainter::LegacyBrushPanelServices(*this).save(); } bool NodePanelBrush::restore() { return pp::panopainter::LegacyBrushPanelServices(*this).restore(); } void NodePanelBrush::clear() { pp::panopainter::LegacyBrushPanelServices(*this).clear(); } void NodePanelBrush::scan() { pp::panopainter::LegacyBrushPanelServices(*this).scan(); } void NodePanelBrush::reload() { pp::panopainter::LegacyBrushPanelServices(*this).reload(); } void NodePanelBrush::added(Node* parent) { m_interacted = false; } // ----------------------------------------------------------------------- std::vector NodePanelBrushPreset::s_panels; Node* NodeBrushPresetItem::clone_instantiate() const { return new NodeBrushPresetItem(); } void NodeBrushPresetItem::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-brush-preset"); color_hover = glm::vec4(.7, .7, .7, 1); color_normal = glm::vec4(.3, .3, .3, 1); m_color = color_normal; m_thumb = find("thumb"); m_caption_size = find("caption-size"); m_preview = find("canvas"); m_preview->m_min_flow = 1.f; } void NodeBrushPresetItem::draw() { m_color = m_mouse_inside ? color_hover : color_normal; m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color; NodeButtonCustom::draw(); } NodePanelBrushPreset::NodePanelBrushPreset() { s_panels.push_back(this); } NodePanelBrushPreset::~NodePanelBrushPreset() { s_panels.erase(std::remove(s_panels.begin(), s_panels.end(), this)); } //--- Node* NodePanelBrushPreset::clone_instantiate() const { return new NodePanelBrushPreset(); } namespace pp::panopainter { class LegacyBrushPresetListServices final : public pp::app::BrushPresetListServices { public: explicit LegacyBrushPresetListServices(NodePanelBrushPreset& owner) : owner_(owner) { } pp::foundation::Status add_current_brush_preset(int target_index) override { if (target_index < 0) { return pp::foundation::Status::out_of_range("brush preset add target is invalid"); } if (!Canvas::I || !Canvas::I->m_current_brush) { return pp::foundation::Status::invalid_argument("current brush must be available to add a preset"); } for (auto p : NodePanelBrushPreset::s_panels) { p->add_brush(std::make_shared(*Canvas::I->m_current_brush)); } return pp::foundation::Status::success(); } void remove_brush_preset( int current_index, int target_index, bool selects_target, bool clears_selection) override { for (auto p : NodePanelBrushPreset::s_panels) { if (current_index < 0 || current_index >= static_cast(p->m_container->m_children.size())) { continue; } const bool new_current = !p->m_current || p->m_container->get_child_index(p->m_current) == current_index; pp::panopainter::destroy_legacy_node(*p->m_container->m_children[current_index]); if (clears_selection) { p->m_current = nullptr; } else if (new_current && selects_target) { p->m_current = static_cast(p->m_container->m_children[target_index].get()); p->m_current->m_selected = true; } } } void move_brush_preset(int from_index, int to_index) override { for (auto p : NodePanelBrushPreset::s_panels) { if (from_index >= 0 && from_index < static_cast(p->m_container->m_children.size())) { p->m_container->move_child(p->m_container->m_children[from_index].get(), to_index); } } } void select_brush_preset(int index, bool notify_brush_changed) override { for (auto p : NodePanelBrushPreset::s_panels) { if (p->m_current) { p->m_current->m_selected = false; } p->m_current = static_cast(p->m_container->get_child_at(index)); p->m_current->m_selected = true; p->m_interacted = true; } if (notify_brush_changed && owner_.on_brush_changed) { owner_.on_brush_changed(&owner_, owner_.m_current->m_brush); } } void clear_brush_presets(bool clears_selection) override { for (auto p : NodePanelBrushPreset::s_panels) { p->m_container->remove_all_children(); if (clears_selection) { p->m_current = nullptr; } } } void update_preset_empty_notification() override { for (auto p : NodePanelBrushPreset::s_panels) { p->m_notification->SetVisibility(p->m_container->m_children.size() == 0); } } void save_preset_list() override { owner_.save(); } private: NodePanelBrushPreset& owner_; }; } // namespace pp::panopainter void NodePanelBrushPreset::execute_preset_list_plan(const pp::app::BrushPresetListPlan& plan) { pp::panopainter::LegacyBrushPresetListServices services(*this); const auto status = pp::app::execute_brush_preset_list_plan(plan, services); if (!status.ok()) { LOG("Brush preset list action failed: %s", status.message); } } void NodePanelBrushPreset::init() { pp::panopainter::LegacyBrushPresetPanelUi::init(*this); } kEventResult NodePanelBrushPreset::handle_event(Event* e) { switch (e->m_type) { case kEventType::MouseLeave: if (!m_interacted) break; // else fall through case kEventType::MouseUpL: if (!m_mouse_inside) { pp::panopainter::release_legacy_mouse_capture(*this); if (m_parent) { pp::panopainter::detach_legacy_node_from_parent(*this); } if (on_popup_close) { on_popup_close(this); } } break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } void NodePanelBrushPreset::handle_click(Node* target) { pp::panopainter::LegacyBrushPresetPanelUi::handle_click(*this, target); } bool NodePanelBrushPreset::save() { return pp::panopainter::LegacyBrushPresetServices(*this).save(); } bool NodePanelBrushPreset::restore() { const auto ok = pp::panopainter::LegacyBrushPresetServices(*this).restore(); if (ok) { m_notification->SetVisibility(m_container->m_children.size() == 0); } return ok; } void NodePanelBrushPreset::add_brush(std::shared_ptr brush) { pp::panopainter::LegacyBrushPresetPanelUi::add_brush(*this, std::move(brush)); } bool NodePanelBrushPreset::export_ppbr(const std::string& path_in, const PPBRInfo& info_data) { return pp::panopainter::LegacyBrushPresetServices(*this).export_ppbr(path_in, info_data); } bool NodePanelBrushPreset::import_ppbr(const std::string& path) { return pp::panopainter::LegacyBrushPresetServices(*this).import_ppbr(path); } bool NodePanelBrushPreset::import_abr(const std::string& path) { return pp::panopainter::LegacyBrushPresetServices(*this).import_abr(path); } bool NodePanelBrushPreset::import_brush(const std::string& path) { return pp::panopainter::LegacyBrushPresetServices(*this).import_brush(path); } void NodePanelBrushPreset::clear_brushes() { const auto plan = pp::app::plan_brush_preset_list_clear( static_cast(m_container->m_children.size())); if (plan) { execute_preset_list_plan(plan.value()); } } void NodePanelBrushPreset::added(Node* parent) { (void)parent; pp::panopainter::LegacyBrushPresetPanelUi::added(*this); }