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;