diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index d19647940554..243ee8f1dd1c 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -47,7 +47,18 @@ The layer's transform. + + If [code]false[/code], any [CanvasItem] under this [CanvasLayer] will be hidden. + Unlike [member CanvasItem.visible], visibility of a [CanvasLayer] isn't propagated to underlying layers. + + + + + Emitted when visibility of the layer is changed. See [member visible]. + + + diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 1c63a4f54fea..f9812263b8f9 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -361,6 +361,17 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll } _update_visibility_color(p_node, item); + } else if (p_node->is_class("CanvasLayer")) { + bool v = p_node->call("is_visible"); + if (v) { + item->add_button(0, get_icon("GuiVisibilityVisible", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility")); + } else { + item->add_button(0, get_icon("GuiVisibilityHidden", "EditorIcons"), BUTTON_VISIBILITY, false, TTR("Toggle Visibility")); + } + + if (!p_node->is_connected("visibility_changed", this, "_node_visibility_changed")) { + p_node->connect("visibility_changed", this, "_node_visibility_changed", varray(p_node)); + } } else if (p_node->is_class("Spatial")) { bool is_locked = p_node->has_meta("_edit_lock_"); if (is_locked) { @@ -470,6 +481,9 @@ void SceneTreeEditor::_node_visibility_changed(Node *p_node) { if (p_node->is_class("CanvasItem")) { visible = p_node->call("is_visible"); CanvasItemEditor::get_singleton()->get_viewport_control()->update(); + } else if (p_node->is_class("CanvasLayer")) { + visible = p_node->call("is_visible"); + CanvasItemEditor::get_singleton()->get_viewport_control()->update(); } else if (p_node->is_class("Spatial")) { visible = p_node->call("is_visible"); } @@ -528,7 +542,7 @@ void SceneTreeEditor::_node_removed(Node *p_node) { p_node->disconnect("script_changed", this, "_node_script_changed"); } - if (p_node->is_class("Spatial") || p_node->is_class("CanvasItem")) { + if (p_node->is_class("Spatial") || p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer")) { if (p_node->is_connected("visibility_changed", this, "_node_visibility_changed")) { p_node->disconnect("visibility_changed", this, "_node_visibility_changed"); } diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index f2413c8d4663..484b6447a988 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -350,31 +350,19 @@ Transform2D CanvasItem::_edit_get_transform() const { #endif bool CanvasItem::is_visible_in_tree() const { - if (!is_inside_tree()) { - return false; - } - - const CanvasItem *p = this; - - while (p) { - if (!p->visible) { - return false; - } - p = p->get_parent_item(); - } - - return true; + return visible && parent_visible_in_tree; } -void CanvasItem::_propagate_visibility_changed(bool p_visible) { +void CanvasItem::_propagate_visibility_changed(bool p_visible, bool p_was_visible) { if (p_visible && first_draw) { //avoid propagating it twice first_draw = false; } + parent_visible_in_tree = p_visible; notification(NOTIFICATION_VISIBILITY_CHANGED); - if (p_visible) { - update(); //todo optimize - } else { + if (visible && p_visible) { + update(); + } else if (!p_visible && (visible || p_was_visible)) { emit_signal(SceneStringNames::get_singleton()->hide); } _block(); @@ -383,43 +371,39 @@ void CanvasItem::_propagate_visibility_changed(bool p_visible) { CanvasItem *c = Object::cast_to(get_child(i)); if (c && c->visible) { //should the toplevels stop propagation? i think so but.. - c->_propagate_visibility_changed(p_visible); + c->_propagate_visibility_changed(p_visible, !p_visible); } } _unblock(); } -void CanvasItem::show() { - if (visible) { +void CanvasItem::set_visible(bool p_visible) { + if (visible == p_visible) { return; } - visible = true; - VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, true); + visible = p_visible; + VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, p_visible); if (!is_inside_tree()) { return; } - _propagate_visibility_changed(true); + _propagate_visibility_changed(p_visible, !p_visible); _change_notify("visible"); } -void CanvasItem::hide() { - if (!visible) { - return; - } - - visible = false; - VisualServer::get_singleton()->canvas_item_set_visible(canvas_item, false); +void CanvasItem::show() { + set_visible(true); +} - if (!is_inside_tree()) { - return; - } +void CanvasItem::hide() { + set_visible(false); +} - _propagate_visibility_changed(false); - _change_notify("visible"); +bool CanvasItem::is_visible() const { + return visible; } CanvasItem *CanvasItem::current_item_drawn = nullptr; @@ -435,7 +419,7 @@ void CanvasItem::_update_callback() { VisualServer::get_singleton()->canvas_item_clear(get_canvas_item()); //todo updating = true - only allow drawing here - if (is_visible_in_tree()) { //todo optimize this!! + if (is_visible_in_tree()) { if (first_draw) { notification(NOTIFICATION_VISIBILITY_CHANGED); first_draw = false; @@ -556,10 +540,20 @@ void CanvasItem::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { ERR_FAIL_COND(!is_inside_tree()); first_draw = true; - if (get_parent()) { - CanvasItem *ci = Object::cast_to(get_parent()); + + Node *parent = get_parent(); + if (parent) { + CanvasItem *ci = Object::cast_to(parent); if (ci) { + parent_visible_in_tree = ci->is_visible_in_tree(); C = ci->children_items.push_back(this); + } else { + CanvasLayer *cl = Object::cast_to(parent); + if (cl) { + parent_visible_in_tree = cl->is_visible(); + } else { + parent_visible_in_tree = true; + } } } _enter_canvas(); @@ -591,6 +585,7 @@ void CanvasItem::_notification(int p_what) { C = nullptr; } global_invalid = true; + parent_visible_in_tree = false; } break; case NOTIFICATION_DRAW: case NOTIFICATION_TRANSFORM_CHANGED: { @@ -601,17 +596,6 @@ void CanvasItem::_notification(int p_what) { } } -void CanvasItem::set_visible(bool p_visible) { - if (p_visible) { - show(); - } else { - hide(); - } -} -bool CanvasItem::is_visible() const { - return visible; -} - void CanvasItem::update() { if (!is_inside_tree()) { return; @@ -1267,6 +1251,7 @@ CanvasItem::CanvasItem() : xform_change(this) { canvas_item = RID_PRIME(VisualServer::get_singleton()->canvas_item_create()); visible = true; + parent_visible_in_tree = false; pending_update = false; modulate = Color(1, 1, 1, 1); self_modulate = Color(1, 1, 1, 1); diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 4d6ded2cf688..a9f69e7710ea 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -162,6 +162,8 @@ VARIANT_ENUM_CAST(CanvasItemMaterial::LightMode) class CanvasItem : public Node { GDCLASS(CanvasItem, Node); + friend class CanvasLayer; + public: enum BlendMode { @@ -191,6 +193,7 @@ class CanvasItem : public Node { bool first_draw; bool visible; + bool parent_visible_in_tree; bool pending_update; bool toplevel; bool drawing; @@ -207,7 +210,7 @@ class CanvasItem : public Node { void _toplevel_raise_self(); - void _propagate_visibility_changed(bool p_visible); + void _propagate_visibility_changed(bool p_visible, bool p_was_visible = false); void _update_callback(); diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp index d186f8580a74..72e0518adf8e 100644 --- a/scene/main/canvas_layer.cpp +++ b/scene/main/canvas_layer.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "canvas_layer.h" +#include "scene/2d/canvas_item.h" #include "viewport.h" void CanvasLayer::set_layer(int p_xform) { @@ -42,6 +43,32 @@ int CanvasLayer::get_layer() const { return layer; } +void CanvasLayer::set_visible(bool p_visible) { + if (p_visible == visible) { + return; + } + + visible = p_visible; + emit_signal("visibility_changed"); + + for (int i = 0; i < get_child_count(); i++) { + CanvasItem *c = Object::cast_to(get_child(i)); + if (c) { + VisualServer::get_singleton()->canvas_item_set_visible(c->get_canvas_item(), p_visible && c->is_visible()); + + if (c->is_visible()) { + c->_propagate_visibility_changed(p_visible); + } else { + c->notification(CanvasItem::NOTIFICATION_VISIBILITY_CHANGED); + } + } + } +} + +bool CanvasLayer::is_visible() const { + return visible; +} + void CanvasLayer::set_transform(const Transform2D &p_xform) { transform = p_xform; locrotscale_dirty = true; @@ -265,6 +292,9 @@ void CanvasLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer); ClassDB::bind_method(D_METHOD("get_layer"), &CanvasLayer::get_layer); + ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasLayer::set_visible); + ClassDB::bind_method(D_METHOD("is_visible"), &CanvasLayer::is_visible); + ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform); @@ -293,6 +323,7 @@ void CanvasLayer::_bind_methods() { ADD_GROUP("Layer", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_GROUP("Transform", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "rotation_degrees", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); @@ -304,6 +335,8 @@ void CanvasLayer::_bind_methods() { ADD_GROUP("Follow Viewport", "follow_viewport"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale"); + + ADD_SIGNAL(MethodInfo("visibility_changed")); } #ifdef TOOLS_ENABLED @@ -326,6 +359,7 @@ CanvasLayer::CanvasLayer() { custom_viewport = nullptr; custom_viewport_id = 0; sort_index = 0; + visible = true; follow_viewport = false; follow_viewport_scale = 1.0; } diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h index 12f1fc3ccb00..e4ce78f3267e 100644 --- a/scene/main/canvas_layer.h +++ b/scene/main/canvas_layer.h @@ -53,6 +53,7 @@ class CanvasLayer : public Node { Viewport *vp; int sort_index; + bool visible; bool follow_viewport; float follow_viewport_scale; @@ -69,6 +70,9 @@ class CanvasLayer : public Node { void set_layer(int p_xform); int get_layer() const; + void set_visible(bool p_visible); + bool is_visible() const; + void set_transform(const Transform2D &p_xform); Transform2D get_transform() const;