Files
panopainter/src/node_panel_brush.cpp

499 lines
14 KiB
C++

#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 <Foundation/Foundation.h>
#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<Serializer::CString>("brush_name", brush_name);
d.value<Serializer::CString>("high_path", high_path);
d.value<Serializer::CString>("thumb_path", thumb_path);
d.value<Serializer::Boolean>("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<Serializer::CString>(brush_name);
d.props["high_path"] = std::make_shared<Serializer::CString>(
str_replace(high_path, App::I->data_path, "{data_path}"));
d.props["thumb_path"] = std::make_shared<Serializer::CString>(
str_replace(thumb_path, App::I->data_path, "{data_path}"));
d.props["m_user_brush"] = std::make_shared<Serializer::Boolean>(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<NodeButtonCustom>("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<NodeButtonCustom>("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<int>(m_container->m_children.size()),
idx,
m_current->m_user_brush);
if (plan) {
execute_texture_list_plan(plan.value());
}
}
};
m_btn_up = find<NodeButtonCustom>("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<int>(m_container->m_children.size()),
idx,
-1);
if (plan) {
execute_texture_list_plan(plan.value());
}
}
};
m_btn_down = find<NodeButtonCustom>("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<int>(m_container->m_children.size()),
idx,
1);
if (plan) {
execute_texture_list_plan(plan.value());
}
}
};
m_container = find<NodeScroll>("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<NodePanelBrush&>(*this)).find_brush(name);
}
std::string NodePanelBrush::get_texture_path(int index) const
{
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_texture_path(index);
}
std::string NodePanelBrush::get_thumb_path(int index) const
{
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*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*> 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<NodeImage>("thumb");
m_caption_size = find<NodeText>("caption-size");
m_preview = find<NodeStrokePreview>("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<Brush>(*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<int>(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<NodeBrushPresetItem*>(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<int>(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<NodeBrushPresetItem*>(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> 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<int>(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);
}