diff options
Diffstat (limited to 'scene')
56 files changed, 1228 insertions, 687 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 5982556c18..4865858b7d 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -247,16 +247,16 @@ SpriteFrames::SpriteFrames() { add_animation(SceneStringNames::get_singleton()->_default); } -void AnimatedSprite::edit_set_pivot(const Point2 &p_pivot) { +void AnimatedSprite::_edit_set_pivot(const Point2 &p_pivot) { set_offset(p_pivot); } -Point2 AnimatedSprite::edit_get_pivot() const { +Point2 AnimatedSprite::_edit_get_pivot() const { return get_offset(); } -bool AnimatedSprite::edit_has_pivot() const { +bool AnimatedSprite::_edit_use_pivot() const { return true; } @@ -509,17 +509,17 @@ bool AnimatedSprite::is_flipped_v() const { return vflip; } -Rect2 AnimatedSprite::get_item_rect() const { +Rect2 AnimatedSprite::_edit_get_rect() const { if (!frames.is_valid() || !frames->has_animation(animation) || frame < 0 || frame >= frames->get_frame_count(animation)) { - return Node2D::get_item_rect(); + return Node2D::_edit_get_rect(); } Ref<Texture> t; if (animation) t = frames->get_frame(animation, frame); if (t.is_null()) - return Node2D::get_item_rect(); + return Node2D::_edit_get_rect(); Size2i s = t->get_size(); Point2 ofs = offset; diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h index 6c660d0381..a8d0db021a 100644 --- a/scene/2d/animated_sprite.h +++ b/scene/2d/animated_sprite.h @@ -149,9 +149,9 @@ protected: virtual void _validate_property(PropertyInfo &property) const; public: - virtual void edit_set_pivot(const Point2 &p_pivot); - virtual Point2 edit_get_pivot() const; - virtual bool edit_has_pivot() const; + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; void set_sprite_frames(const Ref<SpriteFrames> &p_frames); Ref<SpriteFrames> get_sprite_frames() const; @@ -181,7 +181,7 @@ public: void set_modulate(const Color &p_color); Color get_modulate() const; - virtual Rect2 get_item_rect() const; + virtual Rect2 _edit_get_rect() const; virtual String get_configuration_warning() const; AnimatedSprite(); diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp index 2858ddaad5..e4f52a227a 100644 --- a/scene/2d/back_buffer_copy.cpp +++ b/scene/2d/back_buffer_copy.cpp @@ -49,7 +49,7 @@ void BackBufferCopy::_update_copy_mode() { } } -Rect2 BackBufferCopy::get_item_rect() const { +Rect2 BackBufferCopy::_edit_get_rect() const { return rect; } diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h index 2424dd7b19..cfd632d755 100644 --- a/scene/2d/back_buffer_copy.h +++ b/scene/2d/back_buffer_copy.h @@ -52,14 +52,14 @@ protected: static void _bind_methods(); public: + Rect2 _edit_get_rect() const; + void set_rect(const Rect2 &p_rect); Rect2 get_rect() const; void set_copy_mode(CopyMode p_mode); CopyMode get_copy_mode() const; - Rect2 get_item_rect() const; - BackBufferCopy(); ~BackBufferCopy(); }; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index d65a3bfe80..3164344d15 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -52,7 +52,11 @@ void Camera2D::_update_scroll() { if (viewport) { viewport->set_canvas_transform(xform); } - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform); + + Size2 screen_size = viewport->get_visible_rect().size; + Point2 screen_offset = (anchor_mode == ANCHOR_MODE_DRAG_CENTER ? (screen_size * 0.5) : Point2()); + + get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, group_name, "_camera_moved", xform, screen_offset); }; } diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index fa45c61f68..66abe1baa8 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -306,22 +306,7 @@ void CanvasItem::hide() { _change_notify("visible"); } -Variant CanvasItem::edit_get_state() const { - - return Variant(); -} -void CanvasItem::edit_set_state(const Variant &p_state) { -} - -void CanvasItem::edit_set_rect(const Rect2 &p_edit_rect) { - - //used by editors, implement at will -} - -void CanvasItem::edit_rotate(float p_rot) { -} - -Size2 CanvasItem::edit_get_minimum_size() const { +Size2 CanvasItem::_edit_get_minimum_size() const { return Size2(-1, -1); //no limit } @@ -941,15 +926,22 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("_toplevel_raise_self"), &CanvasItem::_toplevel_raise_self); ClassDB::bind_method(D_METHOD("_update_callback"), &CanvasItem::_update_callback); - - ClassDB::bind_method(D_METHOD("edit_set_state", "state"), &CanvasItem::edit_set_state); - ClassDB::bind_method(D_METHOD("edit_get_state"), &CanvasItem::edit_get_state); - ClassDB::bind_method(D_METHOD("edit_set_rect", "rect"), &CanvasItem::edit_set_rect); - ClassDB::bind_method(D_METHOD("edit_rotate", "degrees"), &CanvasItem::edit_rotate); - - ClassDB::bind_method(D_METHOD("get_item_rect"), &CanvasItem::get_item_rect); - ClassDB::bind_method(D_METHOD("get_item_and_children_rect"), &CanvasItem::get_item_and_children_rect); - //ClassDB::bind_method(D_METHOD("get_transform"),&CanvasItem::get_transform); + ClassDB::bind_method(D_METHOD("_edit_set_state", "state"), &CanvasItem::_edit_set_state); + ClassDB::bind_method(D_METHOD("_edit_get_state"), &CanvasItem::_edit_get_state); + + ClassDB::bind_method(D_METHOD("_edit_set_position", "position"), &CanvasItem::_edit_set_position); + ClassDB::bind_method(D_METHOD("_edit_get_position"), &CanvasItem::_edit_get_position); + ClassDB::bind_method(D_METHOD("_edit_use_position"), &CanvasItem::_edit_use_position); + ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect); + ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect); + ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect); + ClassDB::bind_method(D_METHOD("_edit_get_item_and_children_rect"), &CanvasItem::_edit_get_item_and_children_rect); + ClassDB::bind_method(D_METHOD("_edit_set_rotation", "degrees"), &CanvasItem::_edit_set_rotation); + ClassDB::bind_method(D_METHOD("_edit_get_rotation"), &CanvasItem::_edit_get_rotation); + ClassDB::bind_method(D_METHOD("_edit_use_rotation"), &CanvasItem::_edit_use_rotation); + ClassDB::bind_method(D_METHOD("_edit_set_pivot", "pivot"), &CanvasItem::_edit_set_pivot); + ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot); + ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot); ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item); @@ -1119,14 +1111,14 @@ int CanvasItem::get_canvas_layer() const { return 0; } -Rect2 CanvasItem::get_item_and_children_rect() const { +Rect2 CanvasItem::_edit_get_item_and_children_rect() const { - Rect2 rect = get_item_rect(); + Rect2 rect = _edit_get_rect(); for (int i = 0; i < get_child_count(); i++) { CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i)); if (c) { - Rect2 sir = c->get_transform().xform(c->get_item_and_children_rect()); + Rect2 sir = c->get_transform().xform(c->_edit_get_item_and_children_rect()); rect = rect.merge(sir); } } diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h index 1a043c204f..c877a94755 100644 --- a/scene/2d/canvas_item.h +++ b/scene/2d/canvas_item.h @@ -216,11 +216,31 @@ public: /* EDITOR */ - virtual Variant edit_get_state() const; - virtual void edit_set_state(const Variant &p_state); - virtual void edit_set_rect(const Rect2 &p_edit_rect); - virtual void edit_rotate(float p_rot); - virtual Size2 edit_get_minimum_size() const; + virtual void _edit_set_state(const Dictionary &p_state){}; + virtual Dictionary _edit_get_state() const { return Dictionary(); }; + + // Used to move/select the node + virtual void _edit_set_position(const Point2 &p_position){}; + virtual Point2 _edit_get_position() const { return Point2(); }; + virtual bool _edit_use_position() const { return false; }; + + // Used to resize/move/select the node + virtual void _edit_set_rect(const Rect2 &p_rect){}; + virtual Rect2 _edit_get_rect() const { return Rect2(-32, -32, 64, 64); }; + Rect2 _edit_get_item_and_children_rect() const; + virtual bool _edit_use_rect() const { return false; }; + + // Used to rotate the node + virtual void _edit_set_rotation(float p_rotation){}; + virtual float _edit_get_rotation() const { return 0.0; }; + virtual bool _edit_use_rotation() const { return false; }; + + // Used to set a pivot + virtual void _edit_set_pivot(const Point2 &p_pivot){}; + virtual Point2 _edit_get_pivot() const { return Point2(); }; + virtual bool _edit_use_pivot() const { return false; }; + + virtual Size2 _edit_get_minimum_size() const; /* VISIBILITY */ @@ -272,14 +292,11 @@ public: CanvasItem *get_parent_item() const; - virtual Rect2 get_item_rect() const = 0; virtual Transform2D get_transform() const = 0; virtual Transform2D get_global_transform() const; virtual Transform2D get_global_transform_with_canvas() const; - Rect2 get_item_and_children_rect() const; - CanvasItem *get_toplevel() const; _FORCE_INLINE_ RID get_canvas_item() const { return canvas_item; } diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp index a840744c78..92855299ae 100644 --- a/scene/2d/collision_polygon_2d.cpp +++ b/scene/2d/collision_polygon_2d.cpp @@ -243,7 +243,7 @@ CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const { return build_mode; } -Rect2 CollisionPolygon2D::get_item_rect() const { +Rect2 CollisionPolygon2D::_edit_get_rect() const { return aabb; } diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h index c9ec860e36..2fb08a4599 100644 --- a/scene/2d/collision_polygon_2d.h +++ b/scene/2d/collision_polygon_2d.h @@ -69,7 +69,7 @@ public: void set_polygon(const Vector<Point2> &p_polygon); Vector<Point2> get_polygon() const; - virtual Rect2 get_item_rect() const; + virtual Rect2 _edit_get_rect() const; virtual String get_configuration_warning() const; diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp index 0758f4a9bf..f7cb5473e3 100644 --- a/scene/2d/collision_shape_2d.cpp +++ b/scene/2d/collision_shape_2d.cpp @@ -158,7 +158,7 @@ Ref<Shape2D> CollisionShape2D::get_shape() const { return shape; } -Rect2 CollisionShape2D::get_item_rect() const { +Rect2 CollisionShape2D::_edit_get_rect() const { return rect; } diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h index 04203a75b4..4745c659c8 100644 --- a/scene/2d/collision_shape_2d.h +++ b/scene/2d/collision_shape_2d.h @@ -51,9 +51,10 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + void set_shape(const Ref<Shape2D> &p_shape); Ref<Shape2D> get_shape() const; - virtual Rect2 get_item_rect() const; void set_disabled(bool p_disabled); bool is_disabled() const; diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 516acefe2a..d2b987e037 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -32,21 +32,21 @@ #include "engine.h" #include "servers/visual_server.h" -void Light2D::edit_set_pivot(const Point2 &p_pivot) { +void Light2D::_edit_set_pivot(const Point2 &p_pivot) { set_texture_offset(p_pivot); } -Point2 Light2D::edit_get_pivot() const { +Point2 Light2D::_edit_get_pivot() const { return get_texture_offset(); } -bool Light2D::edit_has_pivot() const { +bool Light2D::_edit_use_pivot() const { return true; } -Rect2 Light2D::get_item_rect() const { +Rect2 Light2D::_edit_get_rect() const { if (texture.is_null()) return Rect2(0, 0, 1, 1); diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index f6bc943adb..9b9da8379f 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -84,9 +84,10 @@ protected: static void _bind_methods(); public: - virtual void edit_set_pivot(const Point2 &p_pivot); - virtual Point2 edit_get_pivot() const; - virtual bool edit_has_pivot() const; + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; + virtual Rect2 _edit_get_rect() const; void set_enabled(bool p_enabled); bool is_enabled() const; @@ -151,8 +152,6 @@ public: void set_shadow_smooth(float p_amount); float get_shadow_smooth() const; - virtual Rect2 get_item_rect() const; - String get_configuration_warning() const; Light2D(); diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index c562a4652d..45f780e50e 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -34,35 +34,22 @@ #include "scene/main/viewport.h" #include "servers/visual_server.h" -void Node2D::edit_set_pivot(const Point2 &p_pivot) { -} - -Point2 Node2D::edit_get_pivot() const { - - return Point2(); -} -bool Node2D::edit_has_pivot() const { - - return false; -} +Dictionary Node2D::_edit_get_state() const { -Variant Node2D::edit_get_state() const { - - Array state; - state.push_back(get_position()); - state.push_back(get_rotation()); - state.push_back(get_scale()); + Dictionary state; + state["position"] = get_position(); + state["rotation"] = get_rotation(); + state["scale"] = get_scale(); return state; } -void Node2D::edit_set_state(const Variant &p_state) { +void Node2D::_edit_set_state(const Dictionary &p_state) { - Array state = p_state; - ERR_FAIL_COND(state.size() != 3); + Dictionary state = p_state; + pos = state["position"]; + angle = state["rotation"]; + _scale = state["scale"]; - pos = state[0]; - angle = state[1]; - _scale = state[2]; _update_transform(); _change_notify("rotation"); _change_notify("rotation_degrees"); @@ -70,9 +57,16 @@ void Node2D::edit_set_state(const Variant &p_state) { _change_notify("position"); } -void Node2D::edit_set_rect(const Rect2 &p_edit_rect) { +void Node2D::_edit_set_position(const Point2 &p_position) { + pos = p_position; +} + +Point2 Node2D::_edit_get_position() const { + return pos; +} - Rect2 r = get_item_rect(); +void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) { + Rect2 r = _edit_get_rect(); Vector2 zero_offset; if (r.size.x != 0) @@ -101,14 +95,25 @@ void Node2D::edit_set_rect(const Rect2 &p_edit_rect) { _change_notify("position"); } -void Node2D::edit_rotate(float p_rot) { +bool Node2D::_edit_use_rect() const { + return true; +} - angle += p_rot; +void Node2D::_edit_set_rotation(float p_rotation) { + angle = p_rotation; _update_transform(); _change_notify("rotation"); _change_notify("rotation_degrees"); } +float Node2D::_edit_get_rotation() const { + return angle; +} + +bool Node2D::_edit_use_rotation() const { + return true; +} + void Node2D::_update_xform_values() { pos = _mat.elements[2]; @@ -205,17 +210,6 @@ Transform2D Node2D::get_transform() const { return _mat; } -Rect2 Node2D::get_item_rect() const { - - if (get_script_instance()) { - Variant::CallError err; - Rect2 r = get_script_instance()->call("_get_item_rect", NULL, 0, err); - if (err.error == Variant::CallError::CALL_OK) - return r; - } - return Rect2(Point2(-32, -32), Size2(64, 64)); -} - void Node2D::rotate(float p_radians) { set_rotation(get_rotation() + p_radians); @@ -439,8 +433,6 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_z_as_relative", "enable"), &Node2D::set_z_as_relative); ClassDB::bind_method(D_METHOD("is_z_relative"), &Node2D::is_z_relative); - ClassDB::bind_method(D_METHOD("edit_set_pivot", "pivot"), &Node2D::edit_set_pivot); - ClassDB::bind_method(D_METHOD("get_relative_transform_to_parent", "parent"), &Node2D::get_relative_transform_to_parent); ADD_GROUP("Transform", ""); diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index eca1e96c82..e1e07f2895 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -56,13 +56,16 @@ protected: static void _bind_methods(); public: - virtual Variant edit_get_state() const; - virtual void edit_set_state(const Variant &p_state); - virtual void edit_set_rect(const Rect2 &p_edit_rect); - virtual void edit_rotate(float p_rot); - virtual void edit_set_pivot(const Point2 &p_pivot); - virtual Point2 edit_get_pivot() const; - virtual bool edit_has_pivot() const; + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + + virtual void _edit_set_position(const Point2 &p_position); + virtual Point2 _edit_get_position() const; + virtual void _edit_set_rect(const Rect2 &p_edit_rect); + virtual bool _edit_use_rect() const; + virtual void _edit_set_rotation(float p_rotation); + virtual float _edit_get_rotation() const; + virtual bool _edit_use_rotation() const; void set_position(const Point2 &p_pos); void set_rotation(float p_radians); @@ -85,7 +88,6 @@ public: float get_global_rotation() const; float get_global_rotation_degrees() const; Size2 get_global_scale() const; - virtual Rect2 get_item_rect() const; void set_transform(const Transform2D &p_transform); void set_global_transform(const Transform2D &p_transform); diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp index a13ce6278e..b9012e37b2 100644 --- a/scene/2d/parallax_background.cpp +++ b/scene/2d/parallax_background.cpp @@ -47,10 +47,12 @@ void ParallaxBackground::_notification(int p_what) { } } -void ParallaxBackground::_camera_moved(const Transform2D &p_transform) { +void ParallaxBackground::_camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset) { + + screen_offset = p_screen_offset; set_scroll_scale(p_transform.get_scale().dot(Vector2(0.5, 0.5))); - set_scroll_offset(p_transform.get_origin() / p_transform.get_scale()); + set_scroll_offset(p_transform.get_origin()); } void ParallaxBackground::set_scroll_scale(float p_scale) { @@ -106,9 +108,9 @@ void ParallaxBackground::_update_scroll() { continue; if (ignore_camera_zoom) - l->set_base_offset_and_scale(ofs, 1.0); + l->set_base_offset_and_scale(ofs, 1.0, screen_offset); else - l->set_base_offset_and_scale(ofs, scale); + l->set_base_offset_and_scale(ofs, scale, screen_offset); } } diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h index 0dad1daeab..e37ec0db99 100644 --- a/scene/2d/parallax_background.h +++ b/scene/2d/parallax_background.h @@ -42,6 +42,7 @@ class ParallaxBackground : public CanvasLayer { float scale; Point2 base_offset; Point2 base_scale; + Point2 screen_offset; String group_name; Point2 limit_begin; Point2 limit_end; @@ -51,7 +52,7 @@ class ParallaxBackground : public CanvasLayer { void _update_scroll(); protected: - void _camera_moved(const Transform2D &p_transform); + void _camera_moved(const Transform2D &p_transform, const Point2 &p_screen_offset); void _notification(int p_what); static void _bind_methods(); diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp index 8fe651cb5f..4a69841975 100644 --- a/scene/2d/parallax_layer.cpp +++ b/scene/2d/parallax_layer.cpp @@ -40,7 +40,7 @@ void ParallaxLayer::set_motion_scale(const Size2 &p_scale) { if (pb && is_inside_tree()) { Vector2 ofs = pb->get_final_offset(); float scale = pb->get_scroll_scale(); - set_base_offset_and_scale(ofs, scale); + set_base_offset_and_scale(ofs, scale, screen_offset); } } @@ -57,7 +57,7 @@ void ParallaxLayer::set_motion_offset(const Size2 &p_offset) { if (pb && is_inside_tree()) { Vector2 ofs = pb->get_final_offset(); float scale = pb->get_scroll_scale(); - set_base_offset_and_scale(ofs, scale); + set_base_offset_and_scale(ofs, scale, screen_offset); } } @@ -106,26 +106,28 @@ void ParallaxLayer::_notification(int p_what) { } } -void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_scale) { +void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_scale, const Point2 &p_screen_offset) { + screen_offset = p_screen_offset; if (!is_inside_tree()) return; if (Engine::get_singleton()->is_editor_hint()) return; - Point2 new_ofs = ((orig_offset + p_offset) * motion_scale) * p_scale + motion_offset; + + Point2 new_ofs = (screen_offset + (p_offset - screen_offset) * motion_scale) + motion_offset * p_scale + orig_offset * p_scale; + + Vector2 mirror = Vector2(1, 1); if (mirroring.x) { - double den = mirroring.x * p_scale; - new_ofs.x -= den * ceil(new_ofs.x / den); + mirror.x = -1; } if (mirroring.y) { - double den = mirroring.y * p_scale; - new_ofs.y -= den * ceil(new_ofs.y / den); + mirror.y = -1; } set_position(new_ofs); - set_scale(Vector2(1, 1) * p_scale); + set_scale(mirror * p_scale * orig_scale); } String ParallaxLayer::get_configuration_warning() const { diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h index 95ca27c41a..6feb1fad67 100644 --- a/scene/2d/parallax_layer.h +++ b/scene/2d/parallax_layer.h @@ -43,6 +43,8 @@ class ParallaxLayer : public Node2D { Vector2 mirroring; void _update_mirroring(); + Point2 screen_offset; + protected: void _notification(int p_what); static void _bind_methods(); @@ -57,7 +59,7 @@ public: void set_mirroring(const Size2 &p_mirroring); Size2 get_mirroring() const; - void set_base_offset_and_scale(const Point2 &p_offset, float p_scale); + void set_base_offset_and_scale(const Point2 &p_offset, float p_scale, const Point2 &p_screen_offset); virtual String get_configuration_warning() const; ParallaxLayer(); diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp index 8413be1ca9..55c055e34f 100644 --- a/scene/2d/path_2d.cpp +++ b/scene/2d/path_2d.cpp @@ -107,28 +107,39 @@ void PathFollow2D::_update_transform() { if (!c.is_valid()) return; + if (delta_offset == 0) { + return; + } + float o = offset; if (loop) o = Math::fposmod(o, c->get_baked_length()); Vector2 pos = c->interpolate_baked(o, cubic); + Vector2 offset = Vector2(h_offset, v_offset); + + Transform2D t = get_transform(); + t.set_origin(pos); + if (rotate) { - Vector2 n = (c->interpolate_baked(o + lookahead, cubic) - pos).normalized(); - Vector2 t = -n.tangent(); - pos += n * h_offset; - pos += t * v_offset; + Vector2 t_prev = (pos - c->interpolate_baked(o - delta_offset, cubic)).normalized(); + Vector2 t_cur = (c->interpolate_baked(o + delta_offset, cubic) - pos).normalized(); + + float dot = t_prev.dot(t_cur); + float angle = Math::acos(CLAMP(dot, -1, 1)); + + t.rotate(angle); - set_rotation(t.angle()); + t.translate(offset); } else { - pos.x += h_offset; - pos.y += v_offset; + t.set_origin(t.get_origin() + offset); } - set_position(pos); + set_transform(t); } void PathFollow2D::_notification(int p_what) { @@ -176,8 +187,6 @@ bool PathFollow2D::_set(const StringName &p_name, const Variant &p_value) { set_cubic_interpolation(p_value); } else if (String(p_name) == "loop") { set_loop(p_value); - } else if (String(p_name) == "lookahead") { - set_lookahead(p_value); } else return false; @@ -200,8 +209,6 @@ bool PathFollow2D::_get(const StringName &p_name, Variant &r_ret) const { r_ret = cubic; } else if (String(p_name) == "loop") { r_ret = loop; - } else if (String(p_name) == "lookahead") { - r_ret = lookahead; } else return false; @@ -219,7 +226,6 @@ void PathFollow2D::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::BOOL, "rotate")); p_list->push_back(PropertyInfo(Variant::BOOL, "cubic_interp")); p_list->push_back(PropertyInfo(Variant::BOOL, "loop")); - p_list->push_back(PropertyInfo(Variant::REAL, "lookahead", PROPERTY_HINT_RANGE, "0.001,1024.0,0.001")); } String PathFollow2D::get_configuration_warning() const { @@ -259,7 +265,7 @@ void PathFollow2D::_bind_methods() { } void PathFollow2D::set_offset(float p_offset) { - + delta_offset = p_offset - offset; offset = p_offset; if (path) _update_transform(); @@ -310,16 +316,6 @@ float PathFollow2D::get_unit_offset() const { return 0; } -void PathFollow2D::set_lookahead(float p_lookahead) { - - lookahead = p_lookahead; -} - -float PathFollow2D::get_lookahead() const { - - return lookahead; -} - void PathFollow2D::set_rotate(bool p_rotate) { rotate = p_rotate; @@ -344,11 +340,11 @@ bool PathFollow2D::has_loop() const { PathFollow2D::PathFollow2D() { offset = 0; + delta_offset = 0; h_offset = 0; v_offset = 0; path = NULL; rotate = true; cubic = true; loop = true; - lookahead = 4; } diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h index 88a0abdea9..f5ba3a3d32 100644 --- a/scene/2d/path_2d.h +++ b/scene/2d/path_2d.h @@ -60,9 +60,9 @@ public: private: Path2D *path; real_t offset; + real_t delta_offset; // change in offset since last _update_transform real_t h_offset; real_t v_offset; - real_t lookahead; bool cubic; bool loop; bool rotate; @@ -90,9 +90,6 @@ public: void set_unit_offset(float p_unit_offset); float get_unit_offset() const; - void set_lookahead(float p_lookahead); - float get_lookahead() const; - void set_loop(bool p_loop); bool has_loop() const; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 1287a800e3..1f6127e6eb 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1028,7 +1028,10 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const on_floor = true; floor_velocity = collision.collider_vel; - if (collision.travel.length() < 1 && ABS((lv.x - floor_velocity.x)) < p_slope_stop_min_velocity) { + Vector2 rel_v = lv - floor_velocity; + Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + + if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { Transform2D gt = get_global_transform(); gt.elements[2] -= collision.travel; set_global_transform(gt); diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index b5b5445684..3f2ad19e51 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "polygon_2d.h" -Rect2 Polygon2D::get_item_rect() const { +Rect2 Polygon2D::_edit_get_rect() const { if (rect_cache_dirty) { int l = polygon.size(); @@ -49,16 +49,16 @@ Rect2 Polygon2D::get_item_rect() const { return item_rect; } -void Polygon2D::edit_set_pivot(const Point2 &p_pivot) { +void Polygon2D::_edit_set_pivot(const Point2 &p_pivot) { set_offset(p_pivot); } -Point2 Polygon2D::edit_get_pivot() const { +Point2 Polygon2D::_edit_get_pivot() const { return get_offset(); } -bool Polygon2D::edit_has_pivot() const { +bool Polygon2D::_edit_use_pivot() const { return true; } diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index 0c2c049c18..d09e22f5ff 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -99,11 +99,11 @@ public: //editor stuff - virtual void edit_set_pivot(const Point2 &p_pivot); - virtual Point2 edit_get_pivot() const; - virtual bool edit_has_pivot() const; + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; - virtual Rect2 get_item_rect() const; + virtual Rect2 _edit_get_rect() const; Polygon2D(); }; diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp index cde665d422..1e729bc179 100644 --- a/scene/2d/position_2d.cpp +++ b/scene/2d/position_2d.cpp @@ -38,7 +38,7 @@ void Position2D::_draw_cross() { draw_line(Point2(0, -10), Point2(0, +10), Color(0.5, 1, 0.5)); } -Rect2 Position2D::get_item_rect() const { +Rect2 Position2D::_edit_get_rect() const { return Rect2(Point2(-10, -10), Size2(20, 20)); } diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h index af54fb919a..5961e447df 100644 --- a/scene/2d/position_2d.h +++ b/scene/2d/position_2d.h @@ -42,7 +42,7 @@ protected: void _notification(int p_what); public: - virtual Rect2 get_item_rect() const; + virtual Rect2 _edit_get_rect() const; Position2D(); }; diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp index bf7c5a3ba4..d5fcda90d5 100644 --- a/scene/2d/screen_button.cpp +++ b/scene/2d/screen_button.cpp @@ -133,7 +133,7 @@ void TouchScreenButton::_notification(int p_what) { return; if (shape.is_valid()) { Color draw_col = get_tree()->get_debug_collisions_color(); - Vector2 pos = shape_centered ? get_item_rect().size * 0.5f : Vector2(); + Vector2 pos = shape_centered ? _edit_get_rect().size * 0.5f : Vector2(); draw_set_transform_matrix(get_canvas_transform().translated(pos)); shape->draw(get_canvas_item(), draw_col); } @@ -251,7 +251,7 @@ void TouchScreenButton::_input(const Ref<InputEvent> &p_event) { bool TouchScreenButton::_is_point_inside(const Point2 &p_point) { Point2 coord = (get_global_transform_with_canvas()).affine_inverse().xform(p_point); - Rect2 item_rect = get_item_rect(); + Rect2 item_rect = _edit_get_rect(); bool touched = false; bool check_rect = true; @@ -322,13 +322,13 @@ void TouchScreenButton::_release(bool p_exiting_tree) { } } -Rect2 TouchScreenButton::get_item_rect() const { +Rect2 TouchScreenButton::_edit_get_rect() const { if (texture.is_null()) return Rect2(0, 0, 1, 1); /* if (texture.is_null()) - return CanvasItem::get_item_rect(); + return CanvasItem::_edit_get_rect(); */ return Rect2(Size2(), texture->get_size()); diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h index 7647070b26..2e674c20b4 100644 --- a/scene/2d/screen_button.h +++ b/scene/2d/screen_button.h @@ -102,7 +102,7 @@ public: bool is_pressed() const; - Rect2 get_item_rect() const; + Rect2 _edit_get_rect() const; TouchScreenButton(); }; diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index c53faab5f9..df2265aae9 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -33,16 +33,16 @@ #include "scene/main/viewport.h" #include "scene/scene_string_names.h" -void Sprite::edit_set_pivot(const Point2 &p_pivot) { +void Sprite::_edit_set_pivot(const Point2 &p_pivot) { set_offset(p_pivot); } -Point2 Sprite::edit_get_pivot() const { +Point2 Sprite::_edit_get_pivot() const { return get_offset(); } -bool Sprite::edit_has_pivot() const { +bool Sprite::_edit_use_pivot() const { return true; } @@ -257,13 +257,13 @@ int Sprite::get_hframes() const { return hframes; } -Rect2 Sprite::get_item_rect() const { +Rect2 Sprite::_edit_get_rect() const { if (texture.is_null()) return Rect2(0, 0, 1, 1); /* if (texture.is_null()) - return CanvasItem::get_item_rect(); + return CanvasItem::_edit_get_rect(); */ Size2i s; @@ -368,224 +368,3 @@ Sprite::Sprite() { vframes = 1; hframes = 1; } - -//////////////////////////// VPSPRITE -/// -/// -/// - -#if 0 -void ViewportSprite::edit_set_pivot(const Point2& p_pivot) { - - set_offset(p_pivot); -} - -Point2 ViewportSprite::edit_get_pivot() const { - - return get_offset(); -} -bool ViewportSprite::edit_has_pivot() const { - - return true; -} - -void ViewportSprite::_notification(int p_what) { - - switch(p_what) { - - case NOTIFICATION_ENTER_TREE: { - - if (!viewport_path.is_empty()) { - - Node *n = get_node(viewport_path); - ERR_FAIL_COND(!n); - Viewport *vp=Object::cast_to<Viewport>(n); - ERR_FAIL_COND(!vp); - - Ref<RenderTargetTexture> rtt = vp->get_render_target_texture(); - texture=rtt; - texture->connect("changed",this,"update"); - item_rect_changed(); - } - } break; - case NOTIFICATION_EXIT_TREE: { - - if (texture.is_valid()) { - - texture->disconnect("changed",this,"update"); - texture=Ref<Texture>(); - } - } break; - case NOTIFICATION_DRAW: { - - if (texture.is_null()) - return; - - RID ci = get_canvas_item(); - - /* - texture->draw(ci,Point2()); - break; - */ - - Size2i s; - Rect2i src_rect; - - s = texture->get_size(); - - src_rect.size=s; - - Point2 ofs=offset; - if (centered) - ofs-=s/2; - - if (OS::get_singleton()->get_use_pixel_snap()) { - ofs=ofs.floor(); - } - Rect2 dst_rect(ofs,s); - texture->draw_rect_region(ci,dst_rect,src_rect,modulate); - - } break; - } -} - -void ViewportSprite::set_viewport_path(const NodePath& p_viewport) { - - viewport_path=p_viewport; - update(); - if (!is_inside_tree()) - return; - - if (texture.is_valid()) { - texture->disconnect("changed",this,"update"); - texture=Ref<Texture>(); - } - - if (viewport_path.is_empty()) - return; - - - Node *n = get_node(viewport_path); - ERR_FAIL_COND(!n); - Viewport *vp=Object::cast_to<Viewport>(n); - ERR_FAIL_COND(!vp); - - Ref<RenderTargetTexture> rtt = vp->get_render_target_texture(); - texture=rtt; - - if (texture.is_valid()) { - texture->connect("changed",this,"update"); - } - - item_rect_changed(); - -} - -NodePath ViewportSprite::get_viewport_path() const { - - return viewport_path; -} - -void ViewportSprite::set_centered(bool p_center) { - - centered=p_center; - update(); - item_rect_changed(); -} - -bool ViewportSprite::is_centered() const { - - return centered; -} - -void ViewportSprite::set_offset(const Point2& p_offset) { - - offset=p_offset; - update(); - item_rect_changed(); -} -Point2 ViewportSprite::get_offset() const { - - return offset; -} -void ViewportSprite::set_modulate(const Color& p_color) { - - modulate=p_color; - update(); -} - -Color ViewportSprite::get_modulate() const{ - - return modulate; -} - - -Rect2 ViewportSprite::get_item_rect() const { - - if (texture.is_null()) - return Rect2(0,0,1,1); - /* - if (texture.is_null()) - return CanvasItem::get_item_rect(); - */ - - Size2i s; - - s = texture->get_size(); - Point2 ofs=offset; - if (centered) - ofs-=s/2; - - if (s==Size2(0,0)) - s=Size2(1,1); - - return Rect2(ofs,s); -} - -String ViewportSprite::get_configuration_warning() const { - - if (!has_node(viewport_path) || !Object::cast_to<Viewport>(get_node(viewport_path))) { - return TTR("Path property must point to a valid Viewport node to work. Such Viewport must be set to 'render target' mode."); - } else { - - Node *n = get_node(viewport_path); - if (n) { - Viewport *vp = Object::cast_to<Viewport>(n); - if (!vp->is_set_as_render_target()) { - - return TTR("The Viewport set in the path property must be set as 'render target' in order for this sprite to work."); - } - } - } - - return String(); - -} - -void ViewportSprite::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_viewport_path","path"),&ViewportSprite::set_viewport_path); - ClassDB::bind_method(D_METHOD("get_viewport_path"),&ViewportSprite::get_viewport_path); - - ClassDB::bind_method(D_METHOD("set_centered","centered"),&ViewportSprite::set_centered); - ClassDB::bind_method(D_METHOD("is_centered"),&ViewportSprite::is_centered); - - ClassDB::bind_method(D_METHOD("set_offset","offset"),&ViewportSprite::set_offset); - ClassDB::bind_method(D_METHOD("get_offset"),&ViewportSprite::get_offset); - - ClassDB::bind_method(D_METHOD("set_modulate","modulate"),&ViewportSprite::set_modulate); - ClassDB::bind_method(D_METHOD("get_modulate"),&ViewportSprite::get_modulate); - - ADD_PROPERTYNZ( PropertyInfo( Variant::NODE_PATH, "viewport"), "set_viewport_path","get_viewport_path"); - ADD_PROPERTYNO( PropertyInfo( Variant::BOOL, "centered"), "set_centered","is_centered"); - ADD_PROPERTYNZ( PropertyInfo( Variant::VECTOR2, "offset"), "set_offset","get_offset"); - ADD_PROPERTYNO( PropertyInfo( Variant::COLOR, "modulate"), "set_modulate","get_modulate"); - -} - -ViewportSprite::ViewportSprite() { - - centered=true; - modulate=Color(1,1,1,1); -} -#endif diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h index 64d30325f2..1bef73c0a5 100644 --- a/scene/2d/sprite.h +++ b/scene/2d/sprite.h @@ -62,9 +62,10 @@ protected: virtual void _validate_property(PropertyInfo &property) const; public: - virtual void edit_set_pivot(const Point2 &p_pivot); - virtual Point2 edit_get_pivot() const; - virtual bool edit_has_pivot() const; + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; + virtual Rect2 _edit_get_rect() const; void set_texture(const Ref<Texture> &p_texture); Ref<Texture> get_texture() const; @@ -102,53 +103,7 @@ public: void set_hframes(int p_amount); int get_hframes() const; - virtual Rect2 get_item_rect() const; - Sprite(); }; -#if 0 -class ViewportSprite : public Node2D { - - GDCLASS( ViewportSprite, Node2D ); - - Ref<Texture> texture; - NodePath viewport_path; - - bool centered; - Point2 offset; - Color modulate; - -protected: - - void _notification(int p_what); - - static void _bind_methods(); - -public: - - virtual void edit_set_pivot(const Point2& p_pivot); - virtual Point2 edit_get_pivot() const; - virtual bool edit_has_pivot() const; - - void set_viewport_path(const NodePath& p_viewport); - NodePath get_viewport_path() const; - - void set_centered(bool p_center); - bool is_centered() const; - - void set_offset(const Point2& p_offset); - Point2 get_offset() const; - - void set_modulate(const Color& p_color); - Color get_modulate() const; - - virtual Rect2 get_item_rect() const; - - virtual String get_configuration_warning() const; - - ViewportSprite(); -}; - -#endif #endif // SPRITE_H diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index dd4270ab26..f067b5a187 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -883,7 +883,7 @@ PoolVector<int> TileMap::_get_tile_data() const { return data; } -Rect2 TileMap::get_item_rect() const { +Rect2 TileMap::_edit_get_rect() const { const_cast<TileMap *>(this)->_update_dirty_quadrants(); return rect_cache; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 706b87cec3..9e14ec838a 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -229,7 +229,7 @@ public: void set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x = false, bool p_flip_y = false, bool p_transpose = false); int get_cellv(const Vector2 &p_pos) const; - Rect2 get_item_rect() const; + Rect2 _edit_get_rect() const; void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index b0fd57baf5..298bc2649e 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -83,7 +83,7 @@ void VisibilityNotifier2D::set_rect(const Rect2 &p_rect) { _change_notify("rect"); } -Rect2 VisibilityNotifier2D::get_item_rect() const { +Rect2 VisibilityNotifier2D::_edit_get_rect() const { return rect; } diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h index ee5152978b..6e06833912 100644 --- a/scene/2d/visibility_notifier_2d.h +++ b/scene/2d/visibility_notifier_2d.h @@ -54,13 +54,13 @@ protected: static void _bind_methods(); public: + virtual Rect2 _edit_get_rect() const; + void set_rect(const Rect2 &p_rect); Rect2 get_rect() const; bool is_on_screen() const; - virtual Rect2 get_item_rect() const; - VisibilityNotifier2D(); }; diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp index 324411c5cc..126c07f0be 100644 --- a/scene/3d/light.cpp +++ b/scene/3d/light.cpp @@ -222,6 +222,7 @@ void Light::_bind_methods() { ADD_GROUP("Light", "light_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_ENERGY); + ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); @@ -236,6 +237,7 @@ void Light::_bind_methods() { ADD_GROUP("", ""); BIND_ENUM_CONSTANT(PARAM_ENERGY); + BIND_ENUM_CONSTANT(PARAM_INDIRECT_ENERGY); BIND_ENUM_CONSTANT(PARAM_SPECULAR); BIND_ENUM_CONSTANT(PARAM_RANGE); BIND_ENUM_CONSTANT(PARAM_ATTENUATION); @@ -273,6 +275,7 @@ Light::Light(VisualServer::LightType p_type) { set_cull_mask(0xFFFFFFFF); set_param(PARAM_ENERGY, 1); + set_param(PARAM_INDIRECT_ENERGY, 1); set_param(PARAM_SPECULAR, 0.5); set_param(PARAM_RANGE, 5); set_param(PARAM_ATTENUATION, 1); diff --git a/scene/3d/light.h b/scene/3d/light.h index 37c17cbbe3..8514b429ec 100644 --- a/scene/3d/light.h +++ b/scene/3d/light.h @@ -46,6 +46,7 @@ class Light : public VisualInstance { public: enum Param { PARAM_ENERGY = VS::LIGHT_PARAM_ENERGY, + PARAM_INDIRECT_ENERGY = VS::LIGHT_PARAM_INDIRECT_ENERGY, PARAM_SPECULAR = VS::LIGHT_PARAM_SPECULAR, PARAM_RANGE = VS::LIGHT_PARAM_RANGE, PARAM_ATTENUATION = VS::LIGHT_PARAM_ATTENUATION, diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 915a10328b..2a032f5d96 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -836,9 +836,15 @@ void ParticlesMaterial::_update_shader() { if (flags[FLAG_DISABLE_Z]) { - code += " TRANSFORM[0] = vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n"; - code += " TRANSFORM[1] = vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n"; - code += " TRANSFORM[2] = vec4(0.0,0.0,1.0,0.0);\n"; + if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + code += " if (length(VELOCITY) > 0.0) { TRANSFORM[1].xyz = normalize(VELOCITY); } else { TRANSFORM[1].xyz = normalize(TRANSFORM[1].xyz); }\n"; + code += " TRANSFORM[0].xyz = normalize(cross(TRANSFORM[1].xyz,TRANSFORM[2].xyz));\n"; + code += " TRANSFORM[2] = vec4(0.0,0.0,1.0,0.0);\n"; + } else { + code += " TRANSFORM[0] = vec4(cos(CUSTOM.x),-sin(CUSTOM.x),0.0,0.0);\n"; + code += " TRANSFORM[1] = vec4(sin(CUSTOM.x),cos(CUSTOM.x),0.0,0.0);\n"; + code += " TRANSFORM[2] = vec4(0.0,0.0,1.0,0.0);\n"; + } } else { //orient particle Y towards velocity diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 3976a2ad48..adca78d1d4 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -45,7 +45,7 @@ #endif #include <stdio.h> -Variant Control::edit_get_state() const { +Dictionary Control::_edit_get_state() const { Dictionary s; s["rect"] = get_rect(); @@ -59,22 +59,78 @@ Variant Control::edit_get_state() const { s["anchors"] = anchors; return s; } -void Control::edit_set_state(const Variant &p_state) { +void Control::_edit_set_state(const Dictionary &p_state) { - Dictionary s = p_state; + Dictionary state = p_state; - Rect2 state = s["rect"]; - set_position(state.position); - set_size(state.size); - set_rotation(s["rotation"]); - set_scale(s["scale"]); - Array anchors = s["anchors"]; + Rect2 rect = state["rect"]; + set_position(rect.position); + set_size(rect.size); + set_rotation(state["rotation"]); + set_scale(state["scale"]); + Array anchors = state["anchors"]; set_anchor(MARGIN_LEFT, anchors[0]); set_anchor(MARGIN_TOP, anchors[1]); set_anchor(MARGIN_RIGHT, anchors[2]); set_anchor(MARGIN_BOTTOM, anchors[3]); } +void Control::_edit_set_position(const Point2 &p_position) { + set_position(p_position); +}; + +Point2 Control::_edit_get_position() const { + return get_position(); +}; + +void Control::_edit_set_rect(const Rect2 &p_edit_rect) { + + Transform2D xform = _get_internal_transform(); + + Vector2 new_pos = xform.basis_xform(p_edit_rect.position); + + Vector2 pos = get_position() + new_pos; + + Rect2 new_rect = get_rect(); + new_rect.position = pos.snapped(Vector2(1, 1)); + new_rect.size = p_edit_rect.size.snapped(Vector2(1, 1)); + + set_position(new_rect.position); + set_size(new_rect.size); +} + +Rect2 Control::_edit_get_rect() const { + return Rect2(Point2(), get_size()); +} + +bool Control::_edit_use_rect() const { + return true; +} + +void Control::_edit_set_rotation(float p_rotation) { + set_rotation(p_rotation); +} + +float Control::_edit_get_rotation() const { + return get_rotation(); +} + +bool Control::_edit_use_rotation() const { + return true; +} + +void Control::_edit_set_pivot(const Point2 &p_pivot) { + set_pivot_offset(p_pivot); +} + +Point2 Control::_edit_get_pivot() const { + return get_pivot_offset(); +} + +bool Control::_edit_use_pivot() const { + return true; +} + void Control::set_custom_minimum_size(const Size2 &p_custom) { if (p_custom == data.custom_minimum_size) @@ -96,7 +152,7 @@ Size2 Control::get_combined_minimum_size() const { return minsize; } -Size2 Control::edit_get_minimum_size() const { +Size2 Control::_edit_get_minimum_size() const { return get_combined_minimum_size(); } @@ -110,23 +166,6 @@ Transform2D Control::_get_internal_transform() const { return offset.affine_inverse() * (rot_scale * offset); } -void Control::edit_set_rect(const Rect2 &p_edit_rect) { - - Transform2D xform = _get_internal_transform(); - - // xform[2] += get_position(); - - Vector2 new_pos = xform.basis_xform(p_edit_rect.position); - - Vector2 pos = get_position() + new_pos; - - Rect2 new_rect = get_rect(); - new_rect.position = pos.snapped(Vector2(1, 1)); - new_rect.size = p_edit_rect.size.snapped(Vector2(1, 1)); - - set_position(new_rect.position); - set_size(new_rect.size); -} bool Control::_set(const StringName &p_name, const Variant &p_value) { @@ -1210,7 +1249,7 @@ Size2 Control::get_parent_area_size() const { if (data.parent_canvas_item) { - parent_size = data.parent_canvas_item->get_item_rect().size; + parent_size = data.parent_canvas_item->_edit_get_rect().size; } else { parent_size = get_viewport()->get_visible_rect().size; @@ -1289,7 +1328,7 @@ float Control::_get_parent_range(int p_idx) const { } if (data.parent_canvas_item) { - return data.parent_canvas_item->get_item_rect().size[p_idx & 1]; + return data.parent_canvas_item->_edit_get_rect().size[p_idx & 1]; } else { return get_viewport()->get_visible_rect().size[p_idx & 1]; } @@ -1751,11 +1790,6 @@ Rect2 Control::get_rect() const { return Rect2(get_position(), get_size()); } -Rect2 Control::get_item_rect() const { - - return Rect2(Point2(), get_size()); -} - void Control::add_icon_override(const StringName &p_name, const Ref<Texture> &p_icon) { ERR_FAIL_COND(p_icon.is_null()); @@ -2254,7 +2288,7 @@ Control *Control::_get_focus_neighbour(Margin p_margin, int p_count) { Point2 points[4]; Transform2D xform = get_global_transform(); - Rect2 rect = get_item_rect(); + Rect2 rect = _edit_get_rect(); points[0] = xform.xform(rect.position); points[1] = xform.xform(rect.position + Point2(rect.size.x, 0)); @@ -2313,7 +2347,7 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con Point2 points[4]; Transform2D xform = c->get_global_transform(); - Rect2 rect = c->get_item_rect(); + Rect2 rect = c->_edit_get_rect(); points[0] = xform.xform(rect.position); points[1] = xform.xform(rect.position + Point2(rect.size.x, 0)); diff --git a/scene/gui/control.h b/scene/gui/control.h index 94c484ca50..92d1c969fc 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -271,10 +271,25 @@ public: }; - virtual Variant edit_get_state() const; - virtual void edit_set_state(const Variant &p_state); - virtual void edit_set_rect(const Rect2 &p_edit_rect); - virtual Size2 edit_get_minimum_size() const; + virtual Dictionary _edit_get_state() const; + virtual void _edit_set_state(const Dictionary &p_state); + + virtual void _edit_set_position(const Point2 &p_position); + virtual Point2 _edit_get_position() const; + + virtual void _edit_set_rect(const Rect2 &p_edit_rect); + virtual Rect2 _edit_get_rect() const; + virtual bool _edit_use_rect() const; + + virtual void _edit_set_rotation(float p_rotation); + virtual float _edit_get_rotation() const; + virtual bool _edit_use_rotation() const; + + virtual void _edit_set_pivot(const Point2 &p_pivot); + virtual Point2 _edit_get_pivot() const; + virtual bool _edit_use_pivot() const; + + virtual Size2 _edit_get_minimum_size() const; void accept_event(); @@ -427,7 +442,6 @@ public: CursorShape get_default_cursor_shape() const; virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const; - virtual Rect2 get_item_rect() const; virtual Transform2D get_transform() const; bool is_toplevel_control() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 6ade4fcc38..6aba535572 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -323,6 +323,9 @@ void FileDialog::update_file_list() { while ((item = dir_access->get_next(&isdir)) != "") { + if (item == ".") + continue; + ishidden = dir_access->current_is_hidden(); if (show_hidden || !ishidden) { @@ -344,7 +347,7 @@ void FileDialog::update_file_list() { while (!dirs.empty()) { String &dir_name = dirs.front()->get(); TreeItem *ti = tree->create_item(root); - ti->set_text(0, dir_name + "/"); + ti->set_text(0, dir_name); ti->set_icon(0, folder); Dictionary d; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 946a8c47a3..da52fb39e0 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -964,6 +964,19 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { emit_signal("delete_nodes_request"); accept_event(); } + + Ref<InputEventMagnifyGesture> magnify_gesture = p_ev; + if (magnify_gesture.is_valid()) { + + set_zoom_custom(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); + } + + Ref<InputEventPanGesture> pan_gesture = p_ev; + if (pan_gesture.is_valid()) { + + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } } void GraphEdit::clear_connections() { @@ -975,6 +988,11 @@ void GraphEdit::clear_connections() { void GraphEdit::set_zoom(float p_zoom) { + set_zoom_custom(p_zoom, get_size() / 2); +} + +void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) { + p_zoom = CLAMP(p_zoom, MIN_ZOOM, MAX_ZOOM); if (zoom == p_zoom) return; @@ -982,7 +1000,7 @@ void GraphEdit::set_zoom(float p_zoom) { zoom_minus->set_disabled(zoom == MIN_ZOOM); zoom_plus->set_disabled(zoom == MAX_ZOOM); - Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + get_size() / 2) / zoom; + Vector2 sbofs = (Vector2(h_scroll->get_value(), v_scroll->get_value()) + p_center) / zoom; zoom = p_zoom; top_layer->update(); @@ -992,7 +1010,7 @@ void GraphEdit::set_zoom(float p_zoom) { if (is_visible_in_tree()) { - Vector2 ofs = sbofs * zoom - get_size() / 2; + Vector2 ofs = sbofs * zoom - p_center; h_scroll->set_value(ofs.x); v_scroll->set_value(ofs.y); } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 4656b50133..e8e530848d 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -179,6 +179,7 @@ public: bool is_valid_connection_type(int p_type, int p_with_type) const; void set_zoom(float p_zoom); + void set_zoom_custom(float p_zoom, const Vector2 &p_center); float get_zoom() const; GraphEditFilter *get_top_layer() const { return top_layer; } diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index e9e9dcc859..51ab49e643 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -525,6 +525,11 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { return; } + if (mb->get_button_index() == BUTTON_RIGHT) { + emit_signal("rmb_clicked", mb->get_position()); + + return; + } } if (mb.is_valid() && mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { @@ -708,6 +713,12 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { } } } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * pan_gesture->get_delta().y / 8); + } } void ItemList::ensure_current_is_visible() { @@ -1397,6 +1408,7 @@ void ItemList::_bind_methods() { ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"))); ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected"))); ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index"))); + ADD_SIGNAL(MethodInfo("rmb_clicked", PropertyInfo(Variant::VECTOR2, "at_position"))); GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000); } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 5d3e5ec0e8..f7bf1cd9ea 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1309,6 +1309,7 @@ void LineEdit::set_expand_to_text_length(bool p_enabled) { expand_to_text_length = p_enabled; minimum_size_changed(); + set_window_pos(0); } bool LineEdit::get_expand_to_text_length() const { @@ -1428,7 +1429,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); - ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "expand_to_len"), "set_expand_to_text_length", "get_expand_to_text_length"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); ADD_GROUP("Placeholder", "placeholder_"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 798acb9d52..124c268c8a 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -817,6 +817,16 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } } + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + if (scroll_active) + + vscroll->set_value(vscroll->get_value() + vscroll->get_page() * pan_gesture->get_delta().y * 0.5 / 8); + + return; + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { @@ -877,11 +887,13 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { if (main->first_invalid_line < main->lines.size()) return; + int line = 0; + Item *item = NULL; + bool outside; + _find_click(main, m->get_position(), &item, &line, &outside); + if (selection.click) { - int line = 0; - Item *item = NULL; - _find_click(main, m->get_position(), &item, &line); if (!item) return; // do not update @@ -912,6 +924,22 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { selection.active = true; update(); } + + Variant meta; + if (item && !outside && _find_meta(item, &meta)) { + if (meta_hovering != item) { + if (meta_hovering) { + emit_signal("meta_hover_ended", current_meta); + } + meta_hovering = static_cast<ItemMeta *>(item); + current_meta = meta; + emit_signal("meta_hover_started", meta); + } + } else if (meta_hovering) { + emit_signal("meta_hover_ended", current_meta); + meta_hovering = NULL; + current_meta = false; + } } } @@ -1968,6 +1996,8 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); + ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); + ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index f9e37b1094..1096e3f650 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -225,6 +225,9 @@ private: Align default_align; + ItemMeta *meta_hovering; + Variant current_meta; + void _invalidate_current_line(ItemFrame *p_frame); void _validate_line_caches(ItemFrame *p_frame); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 9022d67a4a..a71a1c5f92 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -180,6 +180,17 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) { time_since_motion = 0; } } + + Ref<InputEventPanGesture> pan_gesture = p_gui_input; + if (pan_gesture.is_valid()) { + + if (h_scroll->is_visible_in_tree()) { + h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + } + if (v_scroll->is_visible_in_tree()) { + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } + } } void ScrollContainer::_update_scrollbar_position() { diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 49823e18fc..1fb0f84223 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -142,91 +142,107 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; - if (rb_pressing && mb.is_valid() && - !mb->is_pressed() && - mb->get_button_index() == BUTTON_LEFT) { + if (mb.is_valid()) { - if (rb_hover != -1) { - //pressed - emit_signal("right_button_pressed", rb_hover); + if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { + + if (scrolling_enabled && buttons_visible) { + if (offset > 0) { + offset--; + update(); + } + } } - rb_pressing = false; - update(); - } + if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) { + if (scrolling_enabled && buttons_visible) { + if (missing_right) { + offset++; + update(); + } + } + } + + if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (cb_pressing && mb.is_valid() && - !mb->is_pressed() && - mb->get_button_index() == BUTTON_LEFT) { + if (rb_hover != -1) { + //pressed + emit_signal("right_button_pressed", rb_hover); + } - if (cb_hover != -1) { - //pressed - emit_signal("tab_close", cb_hover); + rb_pressing = false; + update(); } - cb_pressing = false; - update(); - } + if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (mb.is_valid() && - mb->is_pressed() && - mb->get_button_index() == BUTTON_LEFT) { + if (cb_hover != -1) { + //pressed + emit_signal("tab_close", cb_hover); + } - // clicks - Point2 pos(mb->get_position().x, mb->get_position().y); + cb_pressing = false; + update(); + } - if (buttons_visible) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - Ref<Texture> incr = get_icon("increment"); - Ref<Texture> decr = get_icon("decrement"); + // clicks + Point2 pos(mb->get_position().x, mb->get_position().y); - int limit = get_size().width - incr->get_width() - decr->get_width(); + if (buttons_visible) { - if (pos.x > limit + decr->get_width()) { - if (missing_right) { - offset++; - update(); - } - return; - } else if (pos.x > limit) { - if (offset > 0) { - offset--; - update(); + Ref<Texture> incr = get_icon("increment"); + Ref<Texture> decr = get_icon("decrement"); + + int limit = get_size().width - incr->get_width() - decr->get_width(); + + if (pos.x > limit + decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x > limit) { + if (offset > 0) { + offset--; + update(); + } + return; } - return; } - } - int found = -1; - for (int i = 0; i < tabs.size(); i++) { + int found = -1; + for (int i = 0; i < tabs.size(); i++) { - if (i < offset) - continue; + if (i < offset) + continue; - if (tabs[i].rb_rect.has_point(pos)) { - rb_pressing = true; - update(); - return; - } + if (tabs[i].rb_rect.has_point(pos)) { + rb_pressing = true; + update(); + return; + } - if (tabs[i].cb_rect.has_point(pos)) { - cb_pressing = true; - update(); - return; - } + if (tabs[i].cb_rect.has_point(pos)) { + cb_pressing = true; + update(); + return; + } - if (pos.x >= tabs[i].ofs_cache && pos.x < tabs[i].ofs_cache + tabs[i].size_cache) { - if (!tabs[i].disabled) { - found = i; + if (pos.x >= tabs[i].ofs_cache && pos.x < tabs[i].ofs_cache + tabs[i].size_cache) { + if (!tabs[i].disabled) { + found = i; + } + break; } - break; } - } - if (found != -1) { + if (found != -1) { - set_current_tab(found); - emit_signal("tab_clicked", found); + set_current_tab(found); + emit_signal("tab_clicked", found); + } } } } @@ -440,6 +456,14 @@ int Tabs::get_hovered_tab() const { return hover; } +int Tabs::get_tab_offset() const { + return offset; +} + +bool Tabs::get_offset_buttons_visible() const { + return buttons_visible; +} + void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); @@ -484,6 +508,7 @@ void Tabs::set_tab_right_button(int p_tab, const Ref<Texture> &p_right_button) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs[p_tab].right_button = p_right_button; + _update_cache(); update(); minimum_size_changed(); } @@ -783,6 +808,14 @@ void Tabs::set_min_width(int p_width) { min_width = p_width; } +void Tabs::set_scrolling_enabled(bool p_enabled) { + scrolling_enabled = p_enabled; +} + +bool Tabs::get_scrolling_enabled() const { + return scrolling_enabled; +} + void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); @@ -799,11 +832,15 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref<Texture>())); ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align); ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align); + ClassDB::bind_method(D_METHOD("get_tab_offset"), &Tabs::get_tab_offset); + ClassDB::bind_method(D_METHOD("get_offset_buttons_visible"), &Tabs::get_offset_buttons_visible); ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible); ClassDB::bind_method(D_METHOD("get_tab_rect", "tab_idx"), &Tabs::get_tab_rect); ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &Tabs::move_tab); ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &Tabs::set_tab_close_display_policy); ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy); + ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled); + ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); @@ -814,6 +851,7 @@ void Tabs::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled"); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -841,4 +879,5 @@ Tabs::Tabs() { max_drawn_tab = 0; min_width = 0; + scrolling_enabled = true; } diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 73fa40bbb8..4eb6be3435 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -88,6 +88,7 @@ private: int hover; // hovered tab int min_width; + bool scrolling_enabled; int get_tab_width(int p_idx) const; void _ensure_no_over_offset(); @@ -131,10 +132,16 @@ public: int get_current_tab() const; int get_hovered_tab() const; + int get_tab_offset() const; + bool get_offset_buttons_visible() const; + void remove_tab(int p_idx); void clear_tabs(); + void set_scrolling_enabled(bool p_enabled); + bool get_scrolling_enabled() const; + void ensure_tab_visible(int p_idx); void set_min_width(int p_width); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1b87771fd4..69dc7b21fe 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -274,6 +274,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) { Line line; line.marked = false; line.breakpoint = false; + line.hidden = false; line.width_cache = -1; line.data = p_text; text.insert(p_at, line); @@ -297,9 +298,11 @@ void TextEdit::_update_scrollbars() { int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1; int visible_rows = get_visible_rows(); - int total_rows = text.size(); + int num_rows = MAX(visible_rows, num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs))); + + int total_rows = (is_hiding_enabled() ? get_total_unhidden_rows() : text.size()); if (scroll_past_end_of_file_enabled) { - total_rows += get_visible_rows() - 1; + total_rows += visible_rows - 1; } int vscroll_pixels = v_scroll->get_combined_minimum_size().width; @@ -313,6 +316,10 @@ void TextEdit::_update_scrollbars() { total_width += cache.breakpoint_gutter_width; } + if (draw_fold_gutter) { + total_width += cache.fold_gutter_width; + } + bool use_hscroll = true; bool use_vscroll = true; @@ -347,12 +354,11 @@ void TextEdit::_update_scrollbars() { v_scroll->set_step(1); } - if (fabs(v_scroll->get_value() - (double)cursor.line_ofs) >= 1) { - v_scroll->set_value(cursor.line_ofs); - } - + update_line_scroll_pos(); } else { cursor.line_ofs = 0; + line_scroll_pos = 0; + v_scroll->set_value(0); v_scroll->hide(); } @@ -551,6 +557,13 @@ void TextEdit::_notification(int p_what) { cache.breakpoint_gutter_width = 0; } + if (draw_fold_gutter) { + fold_gutter_width = (get_row_height() * 55) / 100; + cache.fold_gutter_width = fold_gutter_width; + } else { + cache.fold_gutter_width = 0; + } + int line_number_char_count = 0; { @@ -573,7 +586,7 @@ void TextEdit::_notification(int p_what) { RID ci = get_canvas_item(); VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); - int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width; + int xmargin_beg = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width; int xmargin_end = cache.size.width - cache.style_normal->get_margin(MARGIN_RIGHT); //let's do it easy for now: cache.style_normal->draw(ci, Rect2(Point2(), cache.size)); @@ -780,10 +793,22 @@ void TextEdit::_notification(int p_what) { String highlighted_text = get_selection_text(); String line_num_padding = line_numbers_zero_padded ? "0" : " "; + update_line_scroll_pos(); + int line = cursor.line_ofs - 1; for (int i = 0; i < visible_rows; i++) { - int line = i + cursor.line_ofs; + line++; + + if (line < 0 || line >= (int)text.size()) + continue; + + while (is_line_hidden(line)) { + line++; + if (line < 0 || line >= (int)text.size()) { + break; + } + } if (line < 0 || line >= (int)text.size()) continue; @@ -794,7 +819,7 @@ void TextEdit::_notification(int p_what) { int char_ofs = 0; int ofs_y = (i * get_row_height() + cache.line_spacing / 2); if (smooth_scroll_enabled) { - ofs_y -= (v_scroll->get_value() - cursor.line_ofs) * get_row_height(); + ofs_y -= (v_scroll->get_value() - get_line_scroll_pos()) * get_row_height(); } bool prev_is_char = false; @@ -857,6 +882,21 @@ void TextEdit::_notification(int p_what) { } } + // draw fold markers + if (draw_fold_gutter) { + int horizontal_gap = (cache.fold_gutter_width * 30) / 100; + int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w; + if (is_folded(line)) { + int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2; + int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2; + cache.folded_icon->draw(ci, Point2(gutter_left + xofs, ofs_y + yofs), Color(0.8f, 0.8f, 0.8f, 0.8f)); + } else if (can_fold(line)) { + int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3; + int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2; + cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs, ofs_y + yofs), Color(0.8f, 0.8f, 0.8f, 0.8f)); + } + } + if (cache.line_number_w) { String fc = String::num(line + 1); while (fc.length() < line_number_char_count) { @@ -1196,6 +1236,12 @@ void TextEdit::_notification(int p_what) { } char_ofs += char_w; + + if (j == str.length() - 1 && is_folded(line)) { + int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2; + int xofs = cache.folded_eol_icon->get_width() / 2; + cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs, ofs_y + yofs), Color(1, 1, 1, 1)); + } } if (cursor.column == str.length() && cursor.line == line && (char_ofs + char_margin) >= xmargin_beg) { @@ -1538,6 +1584,12 @@ void TextEdit::backspace_at_cursor() { int prev_line = cursor.column ? cursor.line : cursor.line - 1; int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length()); + + if (is_line_hidden(cursor.line)) + set_line_as_hidden(prev_line, true); + if (is_line_set_as_breakpoint(cursor.line)) + set_line_as_breakpoint(prev_line, true); + if (auto_brace_completion_enabled && cursor.column > 0 && _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { @@ -1577,7 +1629,7 @@ void TextEdit::backspace_at_cursor() { } } - cursor_set_line(prev_line); + cursor_set_line(prev_line, true, true); cursor_set_column(prev_column); } @@ -1651,10 +1703,18 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co float rows = p_mouse.y; rows -= cache.style_normal->get_margin(MARGIN_TOP); rows /= get_row_height(); - int row = cursor.line_ofs + (rows + (v_scroll->get_value() - cursor.line_ofs)); + int lsp = get_line_scroll_pos(true); + int row = cursor.line_ofs + (rows + (v_scroll->get_value() - lsp)); + + if (is_hiding_enabled()) { + // row will be offset by the hidden rows + int f_ofs = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(rows + 1, text.size() - cursor.line_ofs)) - 1; + row = cursor.line_ofs + (f_ofs + (v_scroll->get_value() - lsp)); + row = CLAMP(row, 0, text.size() - num_lines_from(text.size() - 1, -1)); + } if (row < 0) - row = 0; + row = 0; //todo int col = 0; @@ -1664,7 +1724,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co col = text[row].size(); } else { - col = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width); + col = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width); col += cursor.x_ofs; col = get_char_pos_for(col, get_line(row)); } @@ -1717,43 +1777,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->is_pressed()) { if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { - if (scrolling) { - target_v_scroll = (target_v_scroll - (3 * mb->get_factor())); - } else { - target_v_scroll = (v_scroll->get_value() - (3 * mb->get_factor())); - } - - if (smooth_scroll_enabled) { - if (target_v_scroll <= 0) { - target_v_scroll = 0; - } - scrolling = true; - set_physics_process(true); - } else { - v_scroll->set_value(target_v_scroll); - } + _scroll_up(3 * mb->get_factor()); } if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) { - if (scrolling) { - target_v_scroll = (target_v_scroll + (3 * mb->get_factor())); - } else { - target_v_scroll = (v_scroll->get_value() + (3 * mb->get_factor())); - } - - if (smooth_scroll_enabled) { - int max_v_scroll = get_line_count() - 1; - if (!scroll_past_end_of_file_enabled) { - max_v_scroll -= get_visible_rows() - 1; - } - - if (target_v_scroll > max_v_scroll) { - target_v_scroll = max_v_scroll; - } - scrolling = true; - set_physics_process(true); - } else { - v_scroll->set_value(target_v_scroll); - } + _scroll_down(3 * mb->get_factor()); } if (mb->get_button_index() == BUTTON_WHEEL_LEFT) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); @@ -1766,6 +1793,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _reset_caret_blink_timer(); int row, col; + update_line_scroll_pos(); _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); if (mb->get_command() && highlighted_word != String()) { @@ -1784,10 +1812,35 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } + // toggle fold on gutter click if can + if (draw_fold_gutter) { + + int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); + int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w; + if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.fold_gutter_width - 3) { + if (is_folded(row)) { + unfold_line(row); + } else if (can_fold(row)) { + fold_line(row); + } + return; + } + } + + // unfold on folded icon click + if (is_folded(row)) { + int line_width = text.get_line_width(row); + line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs; + if (mb->get_position().x > line_width - 3 && mb->get_position().x <= line_width + cache.folded_eol_icon->get_width() + 3) { + unfold_line(row); + return; + } + } + int prev_col = cursor.column; int prev_line = cursor.line; - cursor_set_line(row); + cursor_set_line(row, true, false); cursor_set_column(col); if (mb->get_shift() && (cursor.column != prev_col || cursor.line != prev_line)) { @@ -1884,6 +1937,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } + const Ref<InputEventPanGesture> pan_gesture = p_gui_input; + if (pan_gesture.is_valid()) { + + const real_t delta = pan_gesture->get_delta().y; + if (delta < 0) { + _scroll_up(-delta); + } else { + _scroll_down(delta); + } + h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100); + return; + } + Ref<InputEventMouseMotion> mm = p_gui_input; if (mm.is_valid()) { @@ -2026,22 +2092,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (k->get_scancode() == KEY_DOWN) { - - if (completion_index < completion_options.size() - 1) { - completion_index++; - completion_current = completion_options[completion_index]; - update(); - } - accept_event(); - return; - } - if (k->get_scancode() == KEY_KP_ENTER || k->get_scancode() == KEY_ENTER || k->get_scancode() == KEY_TAB) { _confirm_completion(); accept_event(); - emit_signal("request_completion"); return; } @@ -2191,7 +2245,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { selection.active = false; update(); _remove_text(selection.from_line, selection.from_column, selection.to_line, selection.to_column); - cursor_set_line(selection.from_line); + cursor_set_line(selection.from_line, true, false); cursor_set_column(selection.from_column); update(); } @@ -2241,6 +2295,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } + if (is_folded(cursor.line)) + unfold_line(cursor.line); + bool brace_indent = false; // no need to indent if we are going upwards. @@ -2391,6 +2448,8 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_column(column); } else { + if (cursor.line > 0 && is_line_hidden(cursor.line - 1)) + unfold_line(cursor.line - 1); backspace_at_cursor(); } @@ -2449,7 +2508,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } else if (cursor.column == 0) { if (cursor.line > 0) { - cursor_set_line(cursor.line - 1); + cursor_set_line(cursor.line - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1)); cursor_set_column(text[cursor.line].length()); } } else { @@ -2512,7 +2571,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } else if (cursor.column == text[cursor.line].length()) { if (cursor.line < text.size() - 1) { - cursor_set_line(cursor.line + 1); + cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false); cursor_set_column(0); } } else { @@ -2553,7 +2612,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(0); else #endif - cursor_set_line(cursor_get_line() - 1); + cursor_set_line(cursor_get_line() - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1)); if (k->get_shift()) _post_shift_selection(); @@ -2587,10 +2646,10 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } if (k->get_command()) - cursor_set_line(text.size() - 1); + cursor_set_line(text.size() - 1, true, false); else #endif - cursor_set_line(cursor_get_line() + 1); + cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false); if (k->get_shift()) _post_shift_selection(); @@ -2737,7 +2796,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (k->get_shift()) _pre_shift_selection(); - cursor_set_line(text.size() - 1); + cursor_set_line(text.size() - 1, true, false); if (k->get_shift()) _post_shift_selection(); @@ -2752,7 +2811,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { _pre_shift_selection(); if (k->get_command()) - cursor_set_line(text.size() - 1); + cursor_set_line(text.size() - 1, true, false); cursor_set_column(text[cursor.line].length()); if (k->get_shift()) @@ -2777,7 +2836,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (k->get_shift()) _pre_shift_selection(); - cursor_set_line(cursor_get_line() - get_visible_rows()); + cursor_set_line(cursor_get_line() - num_lines_from(cursor.line, -get_visible_rows()), true, false); if (k->get_shift()) _post_shift_selection(); @@ -2798,7 +2857,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (k->get_shift()) _pre_shift_selection(); - cursor_set_line(cursor_get_line() + get_visible_rows()); + cursor_set_line(cursor_get_line() + num_lines_from(cursor.line, get_visible_rows()), true, false); if (k->get_shift()) _post_shift_selection(); @@ -2984,6 +3043,50 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } +void TextEdit::_scroll_up(real_t p_delta) { + + if (scrolling) { + target_v_scroll = (target_v_scroll - p_delta); + } else { + target_v_scroll = (v_scroll->get_value() - p_delta); + } + + if (smooth_scroll_enabled) { + if (target_v_scroll <= 0) { + target_v_scroll = 0; + } + scrolling = true; + set_physics_process(true); + } else { + v_scroll->set_value(target_v_scroll); + } +} + +void TextEdit::_scroll_down(real_t p_delta) { + + if (scrolling) { + target_v_scroll = (target_v_scroll + p_delta); + } else { + target_v_scroll = (v_scroll->get_value() + p_delta); + } + + if (smooth_scroll_enabled) { + int max_v_scroll = get_total_unhidden_rows(); + if (!scroll_past_end_of_file_enabled) { + max_v_scroll -= get_visible_rows(); + max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows()); + } + + if (target_v_scroll > max_v_scroll) { + target_v_scroll = max_v_scroll; + } + scrolling = true; + set_physics_process(true); + } else { + v_scroll->set_value(target_v_scroll); + } +} + void TextEdit::_pre_shift_selection() { if (!selection.active || selection.selecting_mode == Selection::MODE_NONE) { @@ -3016,8 +3119,9 @@ void TextEdit::_scroll_lines_up() { } // adjust the cursor - if (cursor_get_line() >= (get_visible_rows() + get_v_scroll()) && !selection.active) { - cursor_set_line((get_visible_rows() + get_v_scroll()) - 1, false); + int num_lines = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), get_visible_rows()) - 1; + if (cursor.line >= cursor.line_ofs + num_lines && !selection.active) { + cursor_set_line(cursor.line_ofs + num_lines, false, false); } } @@ -3025,9 +3129,10 @@ void TextEdit::_scroll_lines_down() { scrolling = false; // calculate the maximum vertical scroll position - int max_v_scroll = get_line_count() - 1; + int max_v_scroll = get_total_unhidden_rows(); if (!scroll_past_end_of_file_enabled) { - max_v_scroll -= get_visible_rows() - 1; + max_v_scroll -= get_visible_rows(); + max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows()); } // adjust the vertical scroll @@ -3036,8 +3141,8 @@ void TextEdit::_scroll_lines_down() { } // adjust the cursor - if ((cursor_get_line()) <= get_v_scroll() - 1 && !selection.active) { - cursor_set_line(get_v_scroll(), false); + if (cursor.line <= cursor.line_ofs - 1 && !selection.active) { + cursor_set_line(cursor.line_ofs, false, false); } } @@ -3082,6 +3187,15 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i } } + // if we are just making a new empty line, reset breakpoints and hidden status + if (p_char == 0 && p_text.replace("\r", "") == "\n") { + + text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line)); + text.set_hidden(p_line + 1, text.is_hidden(p_line)); + text.set_breakpoint(p_line, false); + text.set_hidden(p_line, false); + } + r_end_line = p_line + substrings.size() - 1; r_end_column = text[r_end_line].length() - postinsert_text.length(); @@ -3280,6 +3394,7 @@ Size2 TextEdit::get_minimum_size() const { return cache.style_normal->get_minimum_size(); } + int TextEdit::get_visible_rows() const { int total = cache.size.height; @@ -3287,31 +3402,81 @@ int TextEdit::get_visible_rows() const { total /= get_row_height(); return total; } + +int TextEdit::get_total_unhidden_rows() const { + if (!is_hiding_enabled()) + return text.size(); + + int total_unhidden = 0; + for (int i = 0; i < text.size(); i++) { + if (!text.is_hidden(i)) + total_unhidden++; + } + return total_unhidden; +} + +double TextEdit::get_line_scroll_pos(bool p_recalculate) const { + + if (!is_hiding_enabled()) + return cursor.line_ofs; + if (!p_recalculate) + return line_scroll_pos; + + // count num unhidden lines to the cursor line ofs + double new_line_scroll_pos = 0; + int to = CLAMP(cursor.line_ofs, 0, text.size() - 1); + for (int i = 0; i < to; i++) { + if (!text.is_hidden(i)) + new_line_scroll_pos++; + } + return new_line_scroll_pos; +} + +void TextEdit::update_line_scroll_pos() { + + if (!is_hiding_enabled()) { + line_scroll_pos = cursor.line_ofs; + return; + } + + // count num unhidden lines to the cursor line ofs + double new_line_scroll_pos = 0; + int to = CLAMP(cursor.line_ofs, 0, text.size() - 1); + for (int i = 0; i < to; i++) { + if (!text.is_hidden(i)) + new_line_scroll_pos++; + } + line_scroll_pos = new_line_scroll_pos; +} + void TextEdit::adjust_viewport_to_cursor() { scrolling = false; - if (cursor.line_ofs > cursor.line) + if (cursor.line_ofs > cursor.line) { cursor.line_ofs = cursor.line; + } - int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width; + int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space - //printf("rowofs %i, visrows %i, cursor.line %i\n",cursor.line_ofs,get_visible_rows(),cursor.line); - int visible_rows = get_visible_rows(); if (h_scroll->is_visible_in_tree()) visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height()); + int num_rows = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs)); - if (cursor.line >= (cursor.line_ofs + visible_rows)) - cursor.line_ofs = cursor.line - visible_rows; - if (cursor.line < cursor.line_ofs) + // if the cursor is off the screen + if (cursor.line >= (cursor.line_ofs + MAX(num_rows, visible_rows))) { + cursor.line_ofs = cursor.line - (num_lines_from(CLAMP(cursor.line, 0, text.size() - 1), -visible_rows) - 1); + } + if (cursor.line < cursor.line_ofs) { cursor.line_ofs = cursor.line; + } - if (cursor.line_ofs + visible_rows > text.size() && !scroll_past_end_of_file_enabled) { - cursor.line_ofs = text.size() - visible_rows; - v_scroll->set_value(text.size() - visible_rows); + // fixes deleting lines from moving the line ofs in a bad way + if (!scroll_past_end_of_file_enabled && get_total_unhidden_rows() > visible_rows && num_rows < visible_rows) { + cursor.line_ofs = text.size() - 1 - (num_lines_from(text.size() - 1, -visible_rows) - 1); } int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); @@ -3322,6 +3487,11 @@ void TextEdit::adjust_viewport_to_cursor() { if (cursor_x < cursor.x_ofs) cursor.x_ofs = cursor_x; + update_line_scroll_pos(); + if (get_line_scroll_pos() == 0) + v_scroll->set_value(0); + else + v_scroll->set_value(get_line_scroll_pos() + 1); update(); /* get_range()->set_max(text.size()); @@ -3338,7 +3508,10 @@ void TextEdit::center_viewport_to_cursor() { if (cursor.line_ofs > cursor.line) cursor.line_ofs = cursor.line; - int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width; + if (is_line_hidden(cursor.line)) + unfold_line(cursor.line); + + int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width; if (v_scroll->is_visible_in_tree()) visible_width -= v_scroll->get_combined_minimum_size().width; visible_width -= 20; // give it a little more space @@ -3347,9 +3520,8 @@ void TextEdit::center_viewport_to_cursor() { if (h_scroll->is_visible_in_tree()) visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height()); - int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : visible_rows); - cursor.line_ofs = CLAMP(cursor.line - (visible_rows / 2), 0, max_ofs); - + int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : num_lines_from(text.size() - 1, -visible_rows)); + cursor.line_ofs = CLAMP(cursor.line - num_lines_from(cursor.line - visible_rows / 2, -visible_rows / 2), 0, max_ofs); int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]); if (cursor_x > (cursor.x_ofs + visible_width)) @@ -3358,6 +3530,9 @@ void TextEdit::center_viewport_to_cursor() { if (cursor_x < cursor.x_ofs) cursor.x_ofs = cursor_x; + update_line_scroll_pos(); + v_scroll->set_value(get_line_scroll_pos()); + update(); } @@ -3382,7 +3557,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { } } -void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport) { +void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden) { if (setting_row) return; @@ -3394,6 +3569,21 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport) { if (p_row >= (int)text.size()) p_row = (int)text.size() - 1; + if (!p_can_be_hidden) { + if (is_line_hidden(CLAMP(p_row, 0, text.size() - 1))) { + int move_down = num_lines_from(p_row, 1) - 1; + if (p_row + move_down <= text.size() - 1 && !is_line_hidden(p_row + move_down)) { + p_row += move_down; + } else { + int move_up = num_lines_from(p_row, -1) - 1; + if (p_row - move_up > 0 && !is_line_hidden(p_row - move_up)) { + p_row -= move_up; + } else { + WARN_PRINTS(("Cursor set to hidden line " + itos(p_row) + " and there are no nonhidden lines.")); + } + } + } + } cursor.line = p_row; cursor.column = get_char_pos_for(cursor.last_fit_x, get_line(cursor.line)); @@ -3463,8 +3653,11 @@ void TextEdit::_scroll_moved(double p_to_val) { if (h_scroll->is_visible_in_tree()) cursor.x_ofs = h_scroll->get_value(); - if (v_scroll->is_visible_in_tree()) - cursor.line_ofs = v_scroll->get_value(); + if (v_scroll->is_visible_in_tree()) { + double val = v_scroll->get_value(); + cursor.line_ofs = num_lines_from(0, (int)floor(val)) - 1; + line_scroll_pos = (int)floor(val); + } update(); } @@ -3553,10 +3746,43 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { if (highlighted_word != String()) return CURSOR_POINTING_HAND; - int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width; - if ((completion_active && completion_rect.has_point(p_pos)) || p_pos.x < gutter) { + int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width; + if ((completion_active && completion_rect.has_point(p_pos))) { + return CURSOR_ARROW; + } + if (p_pos.x < gutter) { + + int row, col; + _get_mouse_pos(p_pos, row, col); + int left_margin = cache.style_normal->get_margin(MARGIN_LEFT); + + // breakpoint icon + if (draw_breakpoint_gutter && p_pos.x > left_margin && p_pos.x <= left_margin + cache.breakpoint_gutter_width + 3) { + return CURSOR_POINTING_HAND; + } + + // fold icon + int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w; + if (draw_fold_gutter && p_pos.x > gutter_left - 6 && p_pos.x <= gutter_left + cache.fold_gutter_width - 3) { + if (is_folded(row) || can_fold(row)) + return CURSOR_POINTING_HAND; + else + return CURSOR_ARROW; + } return CURSOR_ARROW; + } else { + int row, col; + _get_mouse_pos(p_pos, row, col); + // eol fold icon + if (is_folded(row)) { + int line_width = text.get_line_width(row); + line_width += cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width - cursor.x_ofs; + if (p_pos.x > line_width - 3 && p_pos.x <= line_width + cache.folded_eol_icon->get_width() + 3) { + return CURSOR_POINTING_HAND; + } + } } + return CURSOR_IBEAM; } @@ -3570,6 +3796,7 @@ void TextEdit::set_text(String p_text) { cursor.line = 0; cursor.x_ofs = 0; cursor.line_ofs = 0; + line_scroll_pos = 0; cursor.last_fit_x = 0; cursor_set_line(0); cursor_set_column(0); @@ -3655,6 +3882,7 @@ void TextEdit::_clear() { cursor.line = 0; cursor.x_ofs = 0; cursor.line_ofs = 0; + line_scroll_pos = 0; cursor.last_fit_x = 0; } @@ -3733,6 +3961,9 @@ void TextEdit::_update_caches() { cache.line_spacing = get_constant("line_spacing"); cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_icon("tab"); + cache.folded_icon = get_icon("GuiTreeArrowRight", "EditorIcons"); + cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons"); + cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons"); text.set_font(cache.font); } @@ -4179,6 +4410,198 @@ void TextEdit::get_breakpoints(List<int> *p_breakpoints) const { } } +void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) { + + ERR_FAIL_INDEX(p_line, text.size()); + if (is_hiding_enabled() || !p_hidden) + text.set_hidden(p_line, p_hidden); + update(); +} + +bool TextEdit::is_line_hidden(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), false); + return text.is_hidden(p_line); +} + +void TextEdit::fold_all_lines() { + + for (int i = 0; i < text.size(); i++) { + fold_line(i); + } + _update_scrollbars(); + update(); +} + +void TextEdit::unhide_all_lines() { + + for (int i = 0; i < text.size(); i++) { + text.set_hidden(i, false); + } + _update_scrollbars(); + update(); +} + +int TextEdit::num_lines_from(int p_line_from, int unhidden_amount) const { + + // returns the number of hidden and unhidden lines from p_line_from to p_line_from + amount of visible lines + ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(unhidden_amount)); + + if (!is_hiding_enabled()) + return unhidden_amount; + int num_visible = 0; + int num_total = 0; + if (unhidden_amount >= 0) { + for (int i = p_line_from; i < text.size(); i++) { + num_total++; + if (!is_line_hidden(i)) + num_visible++; + if (num_visible >= unhidden_amount) + break; + } + } else { + unhidden_amount = ABS(unhidden_amount); + for (int i = p_line_from; i >= 0; i--) { + num_total++; + if (!is_line_hidden(i)) + num_visible++; + if (num_visible >= unhidden_amount) + break; + } + } + return num_total; +} + +int TextEdit::get_whitespace_level(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + + // counts number of tabs and spaces before line starts + int whitespace_count = 0; + int line_length = text[p_line].size(); + for (int i = 0; i < line_length - 1; i++) { + if (text[p_line][i] == '\t') { + whitespace_count++; + } else if (text[p_line][i] == ' ') { + whitespace_count++; + } else if (text[p_line][i] == '#') { + break; + } else { + break; + } + } + return whitespace_count; +} + +bool TextEdit::can_fold(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), false); + if (!is_hiding_enabled()) + return false; + if (p_line + 1 >= text.size()) + return false; + if (text[p_line].size() == 0) + return false; + if (is_folded(p_line)) + return false; + if (is_line_hidden(p_line)) + return false; + + int start_indent = get_whitespace_level(p_line); + + for (int i = p_line + 1; i < text.size(); i++) { + if (text[i].size() == 0) + continue; + int next_indent = get_whitespace_level(i); + if (next_indent > start_indent) + return true; + else + return false; + } + + return false; +} + +bool TextEdit::is_folded(int p_line) const { + + ERR_FAIL_INDEX_V(p_line, text.size(), false); + if (p_line + 1 >= text.size() - 1) + return false; + if (!is_line_hidden(p_line) && is_line_hidden(p_line + 1)) + return true; + return false; +} + +void TextEdit::fold_line(int p_line) { + + ERR_FAIL_INDEX(p_line, text.size()); + if (!is_hiding_enabled()) + return; + if (!can_fold(p_line)) + return; + + // hide lines below this one + int start_indent = get_whitespace_level(p_line); + for (int i = p_line + 1; i < text.size(); i++) { + int cur_indent = get_whitespace_level(i); + if (text[i].size() == 0 || cur_indent > start_indent) { + set_line_as_hidden(i, true); + } else { + // exclude trailing empty lines + for (int trail_i = i - 1; trail_i > p_line; trail_i--) { + if (text[trail_i].size() == 0) + set_line_as_hidden(trail_i, false); + else + break; + } + break; + } + } + + // fix selection + if (is_selection_active()) { + if (is_line_hidden(selection.from_line) && is_line_hidden(selection.to_line)) { + deselect(); + } else if (is_line_hidden(selection.from_line)) { + select(p_line, 9999, selection.to_line, selection.to_column); + } else if (is_line_hidden(selection.to_line)) { + select(selection.from_line, selection.from_column, p_line, 9999); + } + } + + // reset cursor + if (is_line_hidden(cursor.line)) { + cursor_set_line(p_line, false, false); + cursor_set_column(get_line(p_line).length(), false); + } + _update_scrollbars(); + update(); +} + +void TextEdit::unfold_line(int p_line) { + + ERR_FAIL_INDEX(p_line, text.size()); + + if (!is_folded(p_line) && !is_line_hidden(p_line)) + return; + int fold_start = p_line; + for (fold_start = p_line; fold_start > 0; fold_start--) { + if (is_folded(fold_start)) + break; + } + fold_start = is_folded(fold_start) ? fold_start : p_line; + + for (int i = fold_start + 1; i < text.size(); i++) { + if (is_line_hidden(i)) { + set_line_as_hidden(i, false); + } else { + break; + } + } + _update_scrollbars(); + update(); +} + int TextEdit::get_line_count() const { return text.size(); @@ -4405,12 +4828,14 @@ void TextEdit::set_v_scroll(int p_scroll) { p_scroll = 0; } if (!scroll_past_end_of_file_enabled) { - if (p_scroll + get_visible_rows() > get_line_count()) { - p_scroll = get_line_count() - get_visible_rows(); + if (p_scroll + get_visible_rows() > get_total_unhidden_rows()) { + int num_rows = num_lines_from(CLAMP(p_scroll, 0, text.size() - 1), MIN(get_visible_rows(), text.size() - 1 - p_scroll)); + p_scroll = text.size() - num_rows; } } v_scroll->set_value(p_scroll); - cursor.line_ofs = p_scroll; + cursor.line_ofs = num_lines_from(0, p_scroll); + line_scroll_pos = p_scroll; } int TextEdit::get_h_scroll() const { @@ -4772,7 +5197,7 @@ void TextEdit::set_line(int line, String new_text) { void TextEdit::insert_at(const String &p_text, int at) { cursor_set_column(0); - cursor_set_line(at); + cursor_set_line(at, false, true); _insert_text(at, 0, p_text + "\n"); } @@ -4820,6 +5245,35 @@ int TextEdit::get_breakpoint_gutter_width() const { return cache.breakpoint_gutter_width; } +void TextEdit::set_draw_fold_gutter(bool p_draw) { + draw_fold_gutter = p_draw; + update(); +} + +bool TextEdit::is_drawing_fold_gutter() const { + return draw_fold_gutter; +} + +void TextEdit::set_fold_gutter_width(int p_gutter_width) { + fold_gutter_width = p_gutter_width; + update(); +} + +int TextEdit::get_fold_gutter_width() const { + return cache.fold_gutter_width; +} + +void TextEdit::set_hiding_enabled(int p_enabled) { + if (!p_enabled) + unhide_all_lines(); + hiding_enabled = p_enabled; + update(); +} + +int TextEdit::is_hiding_enabled() const { + return hiding_enabled; +} + void TextEdit::set_highlight_current_line(bool p_enabled) { highlight_current_line = p_enabled; update(); @@ -4914,7 +5368,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line); ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport"), &TextEdit::cursor_set_line, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true)); ClassDB::bind_method(D_METHOD("cursor_get_column"), &TextEdit::cursor_get_column); ClassDB::bind_method(D_METHOD("cursor_get_line"), &TextEdit::cursor_get_line); @@ -4955,6 +5409,17 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers); ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled); + ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled); + ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled); + ClassDB::bind_method(D_METHOD("set_line_as_hidden", "line", "enable"), &TextEdit::set_line_as_hidden); + ClassDB::bind_method(D_METHOD("is_line_hidden"), &TextEdit::is_line_hidden); + ClassDB::bind_method(D_METHOD("fold_all_lines"), &TextEdit::fold_all_lines); + ClassDB::bind_method(D_METHOD("unhide_all_lines"), &TextEdit::unhide_all_lines); + ClassDB::bind_method(D_METHOD("fold_line", "line"), &TextEdit::fold_line); + ClassDB::bind_method(D_METHOD("unfold_line", "line"), &TextEdit::unfold_line); + ClassDB::bind_method(D_METHOD("can_fold", "line"), &TextEdit::can_fold); + ClassDB::bind_method(D_METHOD("is_folded", "line"), &TextEdit::is_folded); + ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences); ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled); @@ -4988,6 +5453,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled"); ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode"); @@ -5029,6 +5495,8 @@ TextEdit::TextEdit() { cache.line_number_w = 1; cache.breakpoint_gutter_width = 0; breakpoint_gutter_width = 0; + cache.fold_gutter_width = 0; + fold_gutter_width = 0; indent_size = 4; text.set_indent_size(indent_size); @@ -5098,6 +5566,8 @@ TextEdit::TextEdit() { line_length_guideline = false; line_length_guideline_col = 80; draw_breakpoint_gutter = false; + draw_fold_gutter = false; + hiding_enabled = false; next_operation_is_complex = false; scroll_past_end_of_file_enabled = false; auto_brace_completion_enabled = false; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 50f005ed6a..bb9ca87d06 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -73,6 +73,9 @@ class TextEdit : public Control { struct Cache { Ref<Texture> tab_icon; + Ref<Texture> can_fold_icon; + Ref<Texture> folded_icon; + Ref<Texture> folded_eol_icon; Ref<StyleBox> style_normal; Ref<StyleBox> style_focus; Ref<Font> font; @@ -105,6 +108,7 @@ class TextEdit : public Control { int line_spacing; int line_number_w; int breakpoint_gutter_width; + int fold_gutter_width; Size2 size; } cache; @@ -136,6 +140,7 @@ class TextEdit : public Control { int width_cache : 24; bool marked : 1; bool breakpoint : 1; + bool hidden : 1; Map<int, ColorRegionInfo> region_info; String data; }; @@ -160,6 +165,8 @@ class TextEdit : public Control { bool is_marked(int p_line) const { return text[p_line].marked; } void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; } bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; } + void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; } + bool is_hidden(int p_line) const { return text[p_line].hidden; } void insert(int p_at, const String &p_text); void remove(int p_at); int size() const { return text.size(); } @@ -251,6 +258,9 @@ class TextEdit : public Control { int line_length_guideline_col; bool draw_breakpoint_gutter; int breakpoint_gutter_width; + bool draw_fold_gutter; + int fold_gutter_width; + bool hiding_enabled; bool highlight_all_occurrences; bool scroll_past_end_of_file_enabled; @@ -293,9 +303,14 @@ class TextEdit : public Control { int search_result_line; int search_result_col; + double line_scroll_pos; + bool context_menu_enabled; int get_visible_rows() const; + int get_total_unhidden_rows() const; + double get_line_scroll_pos(bool p_recalculate = false) const; + void update_line_scroll_pos(); int get_char_count(); @@ -303,6 +318,7 @@ class TextEdit : public Control { int get_column_x_offset(int p_char, String p_str); void adjust_viewport_to_cursor(); + double get_scroll_line_diff() const; void _scroll_moved(double); void _update_scrollbars(); void _v_scroll_input(); @@ -312,6 +328,9 @@ class TextEdit : public Control { void _update_selection_mode_word(); void _update_selection_mode_line(); + void _scroll_up(real_t p_delta); + void _scroll_down(real_t p_delta); + void _pre_shift_selection(); void _post_shift_selection(); @@ -405,6 +424,18 @@ public: void set_line_as_breakpoint(int p_line, bool p_breakpoint); bool is_line_set_as_breakpoint(int p_line) const; void get_breakpoints(List<int> *p_breakpoints) const; + + void set_line_as_hidden(int p_line, bool p_hidden); + bool is_line_hidden(int p_line) const; + void fold_all_lines(); + void unhide_all_lines(); + int num_lines_from(int p_line_from, int unhidden_amount) const; + int get_whitespace_level(int p_line) const; + bool can_fold(int p_line) const; + bool is_folded(int p_line) const; + void fold_line(int p_line); + void unfold_line(int p_line); + String get_text(); String get_line(int line) const; void set_line(int line, String new_text); @@ -433,7 +464,7 @@ public: void center_viewport_to_cursor(); void cursor_set_column(int p_col, bool p_adjust_viewport = true); - void cursor_set_line(int p_row, bool p_adjust_viewport = true); + void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true); int cursor_get_column() const; int cursor_get_line() const; @@ -538,6 +569,15 @@ public: void set_breakpoint_gutter_width(int p_gutter_width); int get_breakpoint_gutter_width() const; + void set_draw_fold_gutter(bool p_draw); + bool is_drawing_fold_gutter() const; + + void set_fold_gutter_width(int p_gutter_width); + int get_fold_gutter_width() const; + + void set_hiding_enabled(int p_enabled); + int is_hiding_enabled() const; + void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata); void set_completion(bool p_enabled, const Vector<String> &p_prefixes); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f2e5919b5f..9213296c55 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "tree.h" +#include <limits.h> #include "os/input.h" #include "os/keyboard.h" @@ -154,8 +155,17 @@ void TreeItem::set_text(int p_column, String p_text) { if (cells[p_column].mode == TreeItem::CELL_MODE_RANGE || cells[p_column].mode == TreeItem::CELL_MODE_RANGE_EXPRESSION) { - cells[p_column].min = 0; - cells[p_column].max = p_text.get_slice_count(","); + Vector<String> strings = p_text.split(","); + cells[p_column].min = INT_MAX; + cells[p_column].max = INT_MIN; + for (int i = 0; i < strings.size(); i++) { + int value = i; + if (!strings[i].get_slicec(':', 1).empty()) { + value = strings[i].get_slicec(':', 1).to_int(); + } + cells[p_column].min = MIN(cells[p_column].min, value); + cells[p_column].max = MAX(cells[p_column].max, value); + } cells[p_column].step = 0; } _changed_notify(p_column); @@ -1231,8 +1241,18 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int option = (int)p_item->cells[i].val; - String s = p_item->cells[i].text; - s = s.get_slicec(',', option); + String s = RTR("(Other)"); + Vector<String> strings = p_item->cells[i].text.split(","); + for (int i = 0; i < strings.size(); i++) { + int value = i; + if (!strings[i].get_slicec(':', 1).empty()) { + value = strings[i].get_slicec(':', 1).to_int(); + } + if (option == value) { + s = strings[i].get_slicec(':', 0); + break; + } + } if (p_item->cells[i].suffix != String()) s += " " + p_item->cells[i].suffix; @@ -1776,7 +1796,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool for (int i = 0; i < c.text.get_slice_count(","); i++) { String s = c.text.get_slicec(',', i); - popup_menu->add_item(s, i); + popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).empty() ? i : s.get_slicec(':', 1).to_int()); } popup_menu->set_size(Size2(col_width, 0)); @@ -2592,6 +2612,12 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } break; } } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + + v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + } } bool Tree::edit_selected() { @@ -2634,7 +2660,7 @@ bool Tree::edit_selected() { for (int i = 0; i < c.text.get_slice_count(","); i++) { String s = c.text.get_slicec(',', i); - popup_menu->add_item(s, i); + popup_menu->add_item(s.get_slicec(':', 0), s.get_slicec(':', 1).empty() ? i : s.get_slicec(':', 1).to_int()); } popup_menu->set_size(Size2(rect.size.width, 0)); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index d38c688241..01e11962ff 100755 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2067,7 +2067,7 @@ int Node::get_position_in_parent() const { return data.pos; } -Node *Node::duplicate(int p_flags) const { +Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const { Node *node = NULL; @@ -2084,7 +2084,12 @@ Node *Node::duplicate(int p_flags) const { Ref<PackedScene> res = ResourceLoader::load(get_filename()); ERR_FAIL_COND_V(res.is_null(), NULL); - node = res->instance(); + PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED; +#ifdef TOOLS_ENABLED + if (p_flags & DUPLICATE_FROM_EDITOR) + ges = PackedScene::GEN_EDIT_STATE_INSTANCE; +#endif + node = res->instance(ges); ERR_FAIL_COND_V(!node, NULL); instanced = true; @@ -2099,10 +2104,6 @@ Node *Node::duplicate(int p_flags) const { ERR_FAIL_COND_V(!node, NULL); } - if (get_filename() != "") { //an instance - node->set_filename(get_filename()); - } - List<PropertyInfo> plist; get_property_list(&plist); @@ -2138,18 +2139,25 @@ Node *Node::duplicate(int p_flags) const { node->set_name(get_name()); +#ifdef TOOLS_ENABLED + if ((p_flags & DUPLICATE_FROM_EDITOR) && r_duplimap) + r_duplimap->insert(this, node); +#endif + if (p_flags & DUPLICATE_GROUPS) { List<GroupInfo> gi; get_groups(&gi); for (List<GroupInfo>::Element *E = gi.front(); E; E = E->next()) { +#ifdef TOOLS_ENABLED + if ((p_flags & DUPLICATE_FROM_EDITOR) && !E->get().persistent) + continue; +#endif + node->add_to_group(E->get().name, E->get().persistent); } } - if (p_flags & DUPLICATE_SIGNALS) - _duplicate_signals(this, node); - for (int i = 0; i < get_child_count(); i++) { if (get_child(i)->data.parent_owned) @@ -2157,7 +2165,7 @@ Node *Node::duplicate(int p_flags) const { if (instanced && get_child(i)->data.owner == this) continue; //part of instance - Node *dup = get_child(i)->duplicate(p_flags); + Node *dup = get_child(i)->_duplicate(p_flags, r_duplimap); if (!dup) { memdelete(node); @@ -2170,6 +2178,31 @@ Node *Node::duplicate(int p_flags) const { return node; } +Node *Node::duplicate(int p_flags) const { + + Node *dupe = _duplicate(p_flags); + + if (dupe && (p_flags & DUPLICATE_SIGNALS)) { + _duplicate_signals(this, dupe); + } + + return dupe; +} + +#ifdef TOOLS_ENABLED +Node *Node::duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const { + + Node *dupe = _duplicate(DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANCING | DUPLICATE_FROM_EDITOR, &r_duplimap); + + // Duplication of signals must happen after all the node descendants have been copied, + // because re-targeting of connections from some descendant to another is not possible + // if the emitter node comes later in tree order than the receiver + _duplicate_signals(this, dupe); + + return dupe; +} +#endif + void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const { if (get_owner() != get_parent()->get_owner()) @@ -2240,6 +2273,9 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p } } +// Duplication of signals must happen after all the node descendants have been copied, +// because re-targeting of connections from some descendant to another is not possible +// if the emitter node comes later in tree order than the receiver void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { if (this != p_original && (get_owner() != p_original && get_owner() != p_original->get_owner())) @@ -2262,8 +2298,14 @@ void Node::_duplicate_signals(const Node *p_original, Node *p_copy) const { NodePath ptarget = p_original->get_path_to(target); Node *copytarget = p_copy->get_node(ptarget); + // Cannot find a path to the duplicate target, so it seems it's not part + // of the duplicated and not yet parented hierarchy, so at least try to connect + // to the same target as the original + if (!copytarget) + copytarget = target; + if (copy && copytarget) { - copy->connect(E->get().signal, copytarget, E->get().method, E->get().binds, CONNECT_PERSIST); + copy->connect(E->get().signal, copytarget, E->get().method, E->get().binds, E->get().flags); } } } @@ -2308,6 +2350,9 @@ Node *Node::duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const { get_child(i)->_duplicate_and_reown(node, p_reown_map); } + // Duplication of signals must happen after all the node descendants have been copied, + // because re-targeting of connections from some descendant to another is not possible + // if the emitter node comes later in tree order than the receiver _duplicate_signals(this, node); return node; } diff --git a/scene/main/node.h b/scene/main/node.h index e8901f7b6e..bd0b18c87a 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -58,7 +58,10 @@ public: DUPLICATE_SIGNALS = 1, DUPLICATE_GROUPS = 2, DUPLICATE_SCRIPTS = 4, - DUPLICATE_USE_INSTANCING = 8 + DUPLICATE_USE_INSTANCING = 8, +#ifdef TOOLS_ENABLED + DUPLICATE_FROM_EDITOR = 16, +#endif }; enum RPCMode { @@ -169,6 +172,7 @@ private: void _duplicate_signals(const Node *p_original, Node *p_copy) const; void _duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p_reown_map) const; + Node *_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap = NULL) const; Array _get_children() const; Array _get_groups() const; @@ -325,6 +329,9 @@ public: Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const; Node *duplicate_and_reown(const Map<Node *, Node *> &p_reown_map) const; +#ifdef TOOLS_ENABLED + Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const; +#endif //Node *clone_tree() const; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 0a02f471c1..1f539041fd 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2013,6 +2013,30 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } + Ref<InputEventGesture> gesture_event = p_event; + if (gesture_event.is_valid()) { + + Size2 pos = gesture_event->get_position(); + + Control *over = _gui_find_control(pos); + if (over) { + + if (over->can_process()) { + + gesture_event = gesture_event->xformed_by(Transform2D()); //make a copy + if (over == gui.mouse_focus) { + pos = gui.focus_inv_xform.xform(pos); + } else { + pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos); + } + gesture_event->set_position(pos); + _gui_call_input(over, gesture_event); + } + get_tree()->set_input_as_handled(); + return; + } + } + Ref<InputEventScreenDrag> drag_event = p_event; if (drag_event.is_valid()) { diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 06b147ba41..0a886c25b1 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1097,11 +1097,13 @@ void ArrayMesh::_bind_methods() { } void ArrayMesh::reload_from_file() { - for (int i = 0; i < get_surface_count(); i++) { - surface_remove(i); - } + VisualServer::get_singleton()->mesh_clear(mesh); + surfaces.clear(); + clear_blend_shapes(); + Resource::reload_from_file(); - String path = get_path(); + + _change_notify(); } ArrayMesh::ArrayMesh() { |