544 lines
17 KiB
C++
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;
|
|
}
|