Files
panopainter/src/node_panel_layer.cpp

544 lines
17 KiB
C++

#include "pch.h"
#include "log.h"
#include "node_panel_layer.h"
#include "canvas.h"
#include "node_combobox.h"
#include "app.h"
Node* NodeLayer::clone_instantiate() const
{
return new NodeLayer();
}
void NodeLayer::clone_children(Node* dest) const
{
NodeBorder::clone_children(dest);
NodeLayer* n = static_cast<NodeLayer*>(dest);
n->m_label = n->find<NodeText>("label");
n->m_visibility = n->find<NodeCheckBox>("cb");
}
void NodeLayer::clone_copy(Node* dest) const
{
NodeBorder::clone_copy(dest);
NodeLayer* n = (NodeLayer*)dest;
n->m_selected = m_selected;
n->m_label_text = m_label_text;
}
void NodeLayer::init()
{
const auto& m_template = (NodeBorder*)init_template("tpl-layer");
m_color = m_template->m_color;
m_border_color = m_template->m_border_color;
m_thinkness = m_template->m_thinkness;
m_label = find<NodeText>("label");
m_visibility = find<NodeCheckBox>("cb");
}
void NodeLayer::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
{
NodeBorder::parse_attributes(ka, attr);
switch (ka)
{
case kAttribute::Text:
m_label_text = attr->Value();
break;
case kAttribute::Selected:
m_selected = attr->BoolValue();
default:
break;
}
}
void NodeLayer::loaded()
{
NodeBorder::loaded();
if (!m_label_text.empty())
m_label->set_text(m_label_text.c_str());
m_visibility->on_value_changed = [this](Node*, bool checked) {
if (on_visibility_changed)
on_visibility_changed(this, checked);
};
}
kEventResult NodeLayer::handle_event(Event* e)
{
NodeBorder::handle_event(e);
switch (e->m_type)
{
case kEventType::MouseEnter:
break;
case kEventType::MouseLeave:
break;
case kEventType::MouseDownL:
m_selected = true;
if (on_selected)
on_selected(this);
if (on_highlight)
on_highlight(this, true);
mouse_capture();
break;
case kEventType::MouseUpL:
if (on_highlight)
on_highlight(this, false);
mouse_release();
break;
default:
return kEventResult::Available;
break;
}
return kEventResult::Consumed;
}
void NodeLayer::draw()
{
auto c = m_selected ? m_color_selected : m_color_normal;
m_thinkness = m_selected ? 1.f : 0.f;
m_color = m_mouse_inside ? m_color_hover : c;
NodeBorder::draw();
}
void NodeLayer::set_name(const char* s)
{
m_label_text = s;
m_label->set_text(s);
}
Node* NodePanelLayer::clone_instantiate() const
{
return new NodePanelLayer();
}
void NodePanelLayer::init()
{
init_template("tpl-panel-layers");
m_layers_container = find<NodeScroll>("layers-container");
btn_add = find<NodeButtonCustom>("btn-add");
btn_remove = find<NodeButtonCustom>("btn-remove");
btn_up = find<NodeButtonCustom>("btn-up");
btn_down = find<NodeButtonCustom>("btn-down");
btn_duplicate = find<NodeButtonCustom>("btn-duplicate");
btn_duplicate->on_click = [this](Node*) {
std::string next = m_current_layer->m_label_text + "01";
std::regex r(R"(([^\d]*)(\d+)$)");
std::smatch m;
if (std::regex_search(m_current_layer->m_label_text, m, r))
{
auto num = m[2].str();
int count = atoi(num.c_str()) + 1;
char tmp[128];
sprintf(tmp, "%s%0*d", m[1].str().c_str(), (int)num.length(), count);
next = tmp;
}
int source_index = m_layers_container->get_child_index(m_current_layer);
auto l = add_layer(next.c_str(), false, false, nullptr, nullptr, source_index + 1);
if (on_layer_duplicate)
on_layer_duplicate(this, source_index);
if (on_layer_change)
on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
update_attributes();
auto a = new ActionLayerAdd;
a->m_panel = this;
a->m_layer_node = l->shared_from_this();
a->m_layer_order = m_layers_container->get_child_index(l);
a->m_layer_id = Canvas::I->m_layers[a->m_layer_order]->id;
ActionManager::add(a);
};
btn_add->on_click = [this](Node*) {
add_layer(true, true);
};
btn_remove->on_click = [this](Node*) {
if (m_layers.size() == 1)
return; // dont' delete the last layer
remove_layer(m_current_layer);
};
btn_up->on_click = [this](Node*) {
int old_idx = m_layers_container->get_child_index(m_current_layer);
m_layers_container->move_child_offset(m_current_layer, -1);
int new_idx = m_layers_container->get_child_index(m_current_layer);
if (on_layer_order && old_idx != new_idx)
{
on_layer_order(this, old_idx, new_idx);
}
auto a = new ActionLayerMove;
a->m_panel = this;
a->m_layer_node = m_current_layer->shared_from_this();
a->m_offset = -1;
ActionManager::add(a);
};
btn_down->on_click = [this](Node*) {
int old_idx = m_layers_container->get_child_index(m_current_layer);
m_layers_container->move_child_offset(m_current_layer, +1);
int new_idx = m_layers_container->get_child_index(m_current_layer);
if (on_layer_order && old_idx != new_idx)
{
on_layer_order(this, old_idx, new_idx);
}
auto a = new ActionLayerMove;
a->m_panel = this;
a->m_layer_node = m_current_layer->shared_from_this();
a->m_offset = +1;
ActionManager::add(a);
};
m_opacity = find<NodeSliderH>("opacity");
m_opacity->on_value_changed = [this](Node*, float value) {
handle_layer_opacity(m_current_layer, value);
};
m_alpha_lock = find<NodeCheckBox>("alpha-lock");
m_alpha_lock->on_value_changed = [this](Node*, bool locked) {
handle_layer_alpha_lock(m_current_layer, locked);
};
m_blend_mode = find<NodeComboBox>("blend-mode");
m_blend_mode->on_select = [this](Node*, int index) {
handle_layer_blend_mode(m_current_layer, index);
};
}
NodeLayer* NodePanelLayer::add_layer(const char* name, bool add_history /*= true*/, bool create_events /*= false*/,
std::shared_ptr<Layer> layer /*= nullptr*/, std::shared_ptr<NodeLayer> layer_node /*= nullptr*/, int index /*= 0*/)
{
if (index == -1)
index = (int)m_layers_container->m_children.size();
auto l = layer_node ? layer_node : std::make_shared<NodeLayer>();
m_layers_container->add_child(l, index);
l->init();
l->create();
l->loaded();
l->set_name(name);
l->m_visibility->set_value(true);
l->on_selected = std::bind(&NodePanelLayer::handle_layer_selected, this, std::placeholders::_1);
l->on_visibility_changed = std::bind(&NodePanelLayer::handle_layer_visibility, this, std::placeholders::_1, std::placeholders::_2);
l->on_highlight = std::bind(&NodePanelLayer::handle_layer_highlight, this, std::placeholders::_1, std::placeholders::_2);
if (m_current_layer)
m_current_layer->m_selected = false;
m_current_layer = l.get();
m_current_layer->m_selected = true;
m_layers.push_back(l.get());
if (add_history)
{
if (create_events)
{
if (on_layer_add)
on_layer_add(this, nullptr, m_layers_container->get_child_index(m_current_layer));
if (on_layer_change)
on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
update_attributes();
}
auto a = new ActionLayerAdd;
a->m_panel = this;
a->m_layer_node = l->shared_from_this();
a->m_layer_order = m_layers_container->get_child_index(l.get());
a->m_layer_id = Canvas::I->m_layers[a->m_layer_order]->id;
ActionManager::add(a);
}
else if (create_events)
{
if (on_layer_add)
on_layer_add(this, layer, index);
if (on_layer_change)
on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
update_attributes();
}
return l.get();
}
void NodePanelLayer::add_layer(bool add_history /*= true*/, bool create_events /*= false*/)
{
static char s[64];
sprintf(s, "Layer-%d", id_counter++);
add_layer(s, add_history, create_events);
}
NodeLayer* NodePanelLayer::get_layer_at(int index)
{
return static_cast<NodeLayer*>(m_layers_container->get_child_at(index));
}
void NodePanelLayer::remove_layer(NodeLayer* layer, bool add_history /*= true*/)
{
auto it = std::find(m_layers.begin(), m_layers.end(), layer);
auto i = m_layers_container->get_child_index(layer);
int old_idx = i;// (int)std::distance(m_layers.begin(), it);
(*it)->m_selected = false;
auto copy = (*it)->shared_from_this();
m_layers_container->remove_child(layer);
m_layers.erase(it);
i = std::min<int>(i, (int)m_layers.size() - 1);
m_current_layer = (NodeLayer*)m_layers_container->get_child_at(i);
m_current_layer->m_selected = true;
if (add_history)
{
auto a = new ActionLayerRemove;
a->m_panel = this;
a->m_layer_node = copy;
a->m_layer = Canvas::I->m_layers[old_idx];
a->m_layer_order = old_idx;
ActionManager::add(a);
}
if (on_layer_delete)
on_layer_delete(this, old_idx);
if (on_layer_change)
on_layer_change(this, -1, i);
update_attributes();
}
void NodePanelLayer::handle_layer_opacity(NodeLayer* target, float value)
{
if (on_layer_opacity_changed)
on_layer_opacity_changed(this, m_layers_container->get_child_index(target), value);
}
void NodePanelLayer::handle_layer_highlight(NodeLayer* target, bool highlight)
{
if (on_layer_highlight_changed)
on_layer_highlight_changed(this, m_layers_container->get_child_index(target), highlight);
}
void NodePanelLayer::handle_layer_visibility(NodeLayer* target, bool visible)
{
save_history();
if (on_layer_visibility_changed)
on_layer_visibility_changed(this, m_layers_container->get_child_index(target), visible);
}
void NodePanelLayer::handle_layer_alpha_lock(NodeLayer* target, bool locked)
{
save_history();
if (on_layer_alpha_lock_changed)
on_layer_alpha_lock_changed(this, m_layers_container->get_child_index(target), locked);
}
void NodePanelLayer::handle_layer_blend_mode(NodeLayer* target, int mode)
{
save_history();
if (on_layer_blend_mode_changed)
on_layer_blend_mode_changed(this, m_layers_container->get_child_index(target), mode);
}
void NodePanelLayer::handle_layer_selected(NodeLayer* target)
{
if (m_current_layer)
m_current_layer->m_selected = false;
m_current_layer = target;
m_current_layer->m_selected = true;
if (on_layer_change)
on_layer_change(this, -1, m_layers_container->get_child_index(m_current_layer));
update_attributes();
}
void NodePanelLayer::save_history()
{
auto a = new ActionLayerChange;
a->m_panel = this;
for (auto const& l : Canvas::I->m_layers)
{
ActionLayerChange::LayerState s;
s.alpha = l->m_opacity;
s.lock = l->m_alpha_locked;
s.visible = l->m_visible;
s.blend_mode = l->m_blend_mode;
a->m_layers.push_back(s);
}
ActionManager::add(a);
}
void NodePanelLayer::clear()
{
m_layers_container->remove_all_children();
m_layers.clear();
m_current_layer = nullptr;
}
void NodePanelLayer::update_attributes()
{
auto& l = Canvas::I->m_layers[Canvas::I->m_current_layer_idx];
m_opacity->set_value(l->m_opacity);
m_alpha_lock->set_value(l->m_alpha_locked);
m_blend_mode->set_index(l->m_blend_mode);
for (int i = 0; i < Canvas::I->m_layers.size(); i++)
{
m_layers[i]->m_visibility->set_value(Canvas::I->m_layers[i]->m_visible);
}
}
kEventResult NodePanelLayer::handle_event(Event* e)
{
switch (e->m_type)
{
case kEventType::MouseUpL:
if (!m_mouse_inside)
{
mouse_release();
m_parent->remove_child(this);
if (on_popup_close)
on_popup_close(this);
}
break;
default:
return kEventResult::Available;
break;
}
return kEventResult::Available;
}
void NodePanelLayer::merge(int src_index, int dst_index, bool create_history)
{
if (create_history)
{
auto a = new ActionLayerMerge;
a->m_direction = ActionLayerMerge::Direction::Undo;
for (int i = 0; i < 6; i++)
{
a->m_dirty_box[i] = Canvas::I->m_layers[dst_index]->m_dirty_box[i];
a->m_dirty_face[i] = Canvas::I->m_layers[dst_index]->m_dirty_face[i];
}
a->m_snap = std::make_shared<Layer::Snapshot>();
*a->m_snap = Canvas::I->m_layers[dst_index]->snapshot(
Canvas::I->m_layers[src_index]->m_dirty_box, Canvas::I->m_layers[src_index]->m_dirty_face);
a->m_layer = Canvas::I->m_layers[src_index];
a->m_layer_node = std::static_pointer_cast<NodeLayer>(m_layers_container->m_children[src_index]);
a->m_layer_node->m_selected = false;
a->m_panel = std::static_pointer_cast<NodePanelLayer>(shared_from_this());
a->m_src_index = src_index;
a->m_dst_index = dst_index;
ActionManager::add(a);
}
Canvas::I->layer_merge(Canvas::I->m_current_layer_idx, dst_index);
remove_layer((NodeLayer*)m_layers_container->m_children[src_index].get(), false);
}
///////////////////////////////////////////////////////////////////////////////
Action* ActionLayerAdd::get_redo()
{
auto a = new ActionLayerRemove;
a->m_panel = m_panel;
a->m_layer_node = m_layer_node;
a->m_layer = *std::find_if(Canvas::I->m_layers.begin(), Canvas::I->m_layers.end(),
[id=m_layer_id](const auto& x){ return x->id == id; });
a->m_layer_order = m_layer_order;
LOG("ActionLayerAdd::get_redo %s", a->m_layer->m_name.c_str());
return a;
}
void ActionLayerAdd::undo()
{
m_panel->remove_layer((NodeLayer*)m_layer_node.get(), false);
}
///////////////////////////////////////////////////////////////////////////////
Action* ActionLayerRemove::get_redo()
{
auto a = new ActionLayerAdd;
a->m_panel = m_panel;
a->m_layer_node = m_layer_node;
a->m_layer_order = m_layer_order;
a->m_layer_id = m_layer->id;
LOG("ActionLayerRemove::get_redo %s", ((NodeLayer*)m_layer_node.get())->m_label_text.c_str());
return a;
}
void ActionLayerRemove::undo()
{
std::string name = m_layer->m_name;
m_panel->add_layer(name.c_str(), false, true, m_layer,
std::dynamic_pointer_cast<NodeLayer>(m_layer_node), m_layer_order);
LOG("ActionLayerRemove::undo %s", name.c_str());
}
///////////////////////////////////////////////////////////////////////////////
Action* ActionLayerMove::get_redo()
{
auto a = new ActionLayerMove;
a->m_panel = m_panel;
a->m_layer_node = m_layer_node;
a->m_offset = -m_offset;
return a;
}
void ActionLayerMove::undo()
{
int old_idx = m_panel->m_layers_container->get_child_index(m_layer_node.get());
m_panel->m_layers_container->move_child_offset(m_layer_node.get(), -m_offset);
int new_idx = m_panel->m_layers_container->get_child_index(m_layer_node.get());
if (m_panel->on_layer_order && old_idx != new_idx)
{
m_panel->on_layer_order(m_panel, old_idx, new_idx);
}
}
///////////////////////////////////////////////////////////////////////////////
void ActionLayerMerge::undo()
{
if (m_direction == Direction::Undo)
{
Canvas::I->m_layers[m_dst_index]->restore(*m_snap);
for (int i = 0; i < 6; i++)
{
Canvas::I->m_layers[m_dst_index]->m_dirty_box[i] = m_dirty_box[i];
Canvas::I->m_layers[m_dst_index]->m_dirty_face[i] = m_dirty_face[i];
}
auto name = m_layer->m_name;
m_panel->add_layer(name.c_str(), false, true, m_layer, m_layer_node, m_src_index);
}
else if (m_direction == Direction::Redo)
{
m_panel->merge(m_src_index, m_dst_index, false);
}
}
Action* ActionLayerMerge::get_redo()
{
auto a = new ActionLayerMerge;
a->m_dirty_box = m_dirty_box;
a->m_dirty_face = m_dirty_face;
a->m_dst_index = m_dst_index;
a->m_src_index = m_src_index;
a->m_layer = m_layer;
a->m_layer_node = m_layer_node;
a->m_panel = m_panel;
a->m_snap = m_snap;
a->m_direction = m_direction == Direction::Undo ? Direction::Redo : Direction::Undo;
return a;
}
///////////////////////////////////////////////////////////////////////////////
void ActionLayerChange::undo()
{
for (int i = 0; i < m_layers.size(); i++)
{
auto& l = Canvas::I->m_layers[i];
l->m_opacity = m_layers[i].alpha;
l->m_alpha_locked = m_layers[i].lock;
l->m_visible = m_layers[i].visible;
l->m_blend_mode = m_layers[i].blend_mode;
}
m_panel->update_attributes();
}
Action* ActionLayerChange::get_redo()
{
auto a = new ActionLayerChange;
a->m_panel = m_panel;
for (auto const& l : Canvas::I->m_layers)
{
LayerState s;
s.alpha = l->m_opacity;
s.lock = l->m_alpha_locked;
s.visible = l->m_visible;
s.blend_mode = l->m_blend_mode;
a->m_layers.push_back(s);
}
return a;
}