#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(dest); n->m_label = n->find("label"); n->m_visibility = n->find("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() { init_template_file("data/dialogs/panel-layers.xml", "tpl-layer"); m_label = find("label"); m_visibility = find("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_file("data/dialogs/panel-layers.xml", "tpl-panel-layers"); m_layers_container = find("layers-container"); btn_add = find("btn-add"); btn_remove = find("btn-remove"); btn_up = find("btn-up"); btn_down = find("btn-down"); btn_duplicate = find("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("opacity"); m_opacity->on_value_changed = [this](Node*, float value) { handle_layer_opacity(m_current_layer, value); }; m_alpha_lock = find("alpha-lock"); m_alpha_lock->on_value_changed = [this](Node*, bool locked) { handle_layer_alpha_lock(m_current_layer, locked); }; m_blend_mode = find("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 /*= nullptr*/, std::shared_ptr 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(); 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); // reset selected state for (const auto& c : m_layers_container->m_children) ((NodeLayer*)c.get())->m_selected = false; 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(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(i, (int)m_layers.size() - 1); // reset selected state for (const auto& c : m_layers_container->m_children) ((NodeLayer*)c.get())->m_selected = false; 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]->box(i); a->m_dirty_face[i] = Canvas::I->m_layers[dst_index]->face(i); } a->m_snap = std::make_shared(); *a->m_snap = Canvas::I->m_layers[dst_index]->snapshot(-1, &Canvas::I->m_layers[dst_index]->frame().m_dirty_box, &Canvas::I->m_layers[dst_index]->frame().m_dirty_face); a->m_layer = Canvas::I->m_layers[src_index]; a->m_layer_node = std::static_pointer_cast(m_layers_container->m_children[src_index]); a->m_layer_node->m_selected = false; a->m_panel = std::static_pointer_cast(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(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]->box(i) = m_dirty_box[i]; Canvas::I->m_layers[m_dst_index]->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 = reverse_direction(); 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; }