diff options
Diffstat (limited to 'scene')
64 files changed, 2202 insertions, 573 deletions
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index b605be47df..b38fbfe981 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -602,9 +602,7 @@ void CanvasItem::_notification(int p_what) { } global_invalid = true; } break; - case NOTIFICATION_DRAW: { - - } break; + case NOTIFICATION_DRAW: case NOTIFICATION_TRANSFORM_CHANGED: { } break; @@ -641,6 +639,9 @@ void CanvasItem::update() { void CanvasItem::set_modulate(const Color &p_modulate) { + if (modulate == p_modulate) + return; + modulate = p_modulate; VisualServer::get_singleton()->canvas_item_set_modulate(canvas_item, modulate); } @@ -679,6 +680,9 @@ CanvasItem *CanvasItem::get_parent_item() const { void CanvasItem::set_self_modulate(const Color &p_self_modulate) { + if (self_modulate == p_self_modulate) + return; + self_modulate = p_self_modulate; VisualServer::get_singleton()->canvas_item_set_self_modulate(canvas_item, self_modulate); } @@ -689,6 +693,9 @@ Color CanvasItem::get_self_modulate() const { void CanvasItem::set_light_mask(int p_light_mask) { + if (light_mask == p_light_mask) + return; + light_mask = p_light_mask; VS::get_singleton()->canvas_item_set_light_mask(canvas_item, p_light_mask); } diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 202c7c9cf2..228b67990c 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -376,11 +376,12 @@ void CollisionObject2D::set_only_update_transform_changes(bool p_enable) { void CollisionObject2D::_update_pickable() { if (!is_inside_tree()) return; - bool pickable = this->pickable && is_inside_tree() && is_visible_in_tree(); + + bool is_pickable = pickable && is_visible_in_tree(); if (area) - Physics2DServer::get_singleton()->area_set_pickable(rid, pickable); + Physics2DServer::get_singleton()->area_set_pickable(rid, is_pickable); else - Physics2DServer::get_singleton()->body_set_pickable(rid, pickable); + Physics2DServer::get_singleton()->body_set_pickable(rid, is_pickable); } String CollisionObject2D::get_configuration_warning() const { diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 9fe5fb98b6..6436b3878f 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -155,7 +155,7 @@ void LineBuilder::build() { texture_mode == Line2D::LINE_TEXTURE_STRETCH; if (distance_required) { total_distance = calculate_total_distance(points); - //Ajust totalDistance. + //Adjust totalDistance. // The line's outer length will be a little higher due to begin and end caps if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) { if (retrieve_curve) diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp index e389d5f98f..678db85ff0 100644 --- a/scene/2d/navigation_polygon.cpp +++ b/scene/2d/navigation_polygon.cpp @@ -271,7 +271,7 @@ void NavigationPolygon::make_polygons_from_outlines() { struct Polygon p; - for (int i = 0; i < tp.GetNumPoints(); i++) { + for (int64_t i = 0; i < tp.GetNumPoints(); i++) { Map<Vector2, int>::Element *E = points.find(tp[i]); if (!E) { diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 7cc937a64a..28f7243c44 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1209,7 +1209,7 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_ return colliding; } -//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. +//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { @@ -1446,6 +1446,14 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) { void KinematicBody2D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { last_valid_transform = get_global_transform(); + + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector2(); } if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 2cd05b5c50..15423f8c5e 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1944,6 +1944,7 @@ TileMap::TileMap() { quadrant_order_dirty = false; quadrant_size = 16; cell_size = Size2(64, 64); + custom_transform = Transform2D(64, 0, 0, 64, 0, 0); collision_layer = 1; collision_mask = 1; friction = 1; diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index 63301fc226..735b393171 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -105,7 +105,8 @@ void CollisionObject::_mouse_exit() { void CollisionObject::_update_pickable() { if (!is_inside_tree()) return; - bool pickable = ray_pickable && is_inside_tree() && is_visible_in_tree(); + + bool pickable = ray_pickable && is_visible_in_tree(); if (area) PhysicsServer::get_singleton()->area_set_ray_pickable(rid, pickable); else diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index 89072519d5..50ca466df3 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -149,12 +149,38 @@ Ref<Mesh> MeshInstance::get_mesh() const { void MeshInstance::_resolve_skeleton_path() { - if (skeleton_path.is_empty()) + Ref<SkinReference> new_skin_reference; + + if (!skeleton_path.is_empty()) { + Skeleton *skeleton = Object::cast_to<Skeleton>(get_node(skeleton_path)); + if (skeleton) { + new_skin_reference = skeleton->register_skin(skin); + if (skin.is_null()) { + //a skin was created for us + skin = new_skin_reference->get_skin(); + _change_notify(); + } + } + } + + skin_ref = new_skin_reference; + + if (skin_ref.is_valid()) { + VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), skin_ref->get_skeleton()); + } else { + VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), RID()); + } +} + +void MeshInstance::set_skin(const Ref<Skin> &p_skin) { + skin = p_skin; + if (!is_inside_tree()) return; + _resolve_skeleton_path(); +} - Skeleton *skeleton = Object::cast_to<Skeleton>(get_node(skeleton_path)); - if (skeleton) - VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), skeleton->get_skeleton()); +Ref<Skin> MeshInstance::get_skin() const { + return skin; } void MeshInstance::set_skeleton_path(const NodePath &p_skeleton) { @@ -365,6 +391,8 @@ void MeshInstance::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance::get_mesh); ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &MeshInstance::set_skeleton_path); ClassDB::bind_method(D_METHOD("get_skeleton_path"), &MeshInstance::get_skeleton_path); + ClassDB::bind_method(D_METHOD("set_skin", "skin"), &MeshInstance::set_skin); + ClassDB::bind_method(D_METHOD("get_skin"), &MeshInstance::get_skin); ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance::get_surface_material_count); ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance::set_surface_material); @@ -380,6 +408,7 @@ void MeshInstance::_bind_methods() { ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path"); } diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 8b690b0c21..77ead75dd3 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -31,8 +31,10 @@ #ifndef MESH_INSTANCE_H #define MESH_INSTANCE_H +#include "scene/3d/skeleton.h" #include "scene/3d/visual_instance.h" #include "scene/resources/mesh.h" +#include "scene/resources/skin.h" class MeshInstance : public GeometryInstance { @@ -40,6 +42,8 @@ class MeshInstance : public GeometryInstance { protected: Ref<Mesh> mesh; + Ref<Skin> skin; + Ref<SkinReference> skin_ref; NodePath skeleton_path; struct BlendShapeTrack { @@ -70,6 +74,9 @@ public: void set_mesh(const Ref<Mesh> &p_mesh); Ref<Mesh> get_mesh() const; + void set_skin(const Ref<Skin> &p_skin); + Ref<Skin> get_skin() const; + void set_skeleton_path(const NodePath &p_skeleton); NodePath get_skeleton_path(); diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index ebac968cb4..71040d2d68 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1137,7 +1137,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in return colliding; } -//so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. +//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { @@ -1387,6 +1387,18 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { return slide_colliders[p_bounce]; } +void KinematicBody::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector3(); + } +} + void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); @@ -2170,7 +2182,7 @@ void PhysicalBone::_notification(int p_what) { void PhysicalBone::_direct_state_changed(Object *p_state) { - if (!simulate_physics) { + if (!simulate_physics || !_internal_simulate_physics) { return; } @@ -2193,7 +2205,7 @@ void PhysicalBone::_direct_state_changed(Object *p_state) { // Update skeleton if (parent_skeleton) { if (-1 != bone_id) { - parent_skeleton->set_bone_global_pose(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse)); + parent_skeleton->set_bone_global_pose_override(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse), 1.0, true); } } } @@ -2704,7 +2716,6 @@ void PhysicalBone::_start_physics_simulation() { PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); - parent_skeleton->set_bone_ignore_animation(bone_id, true); _internal_simulate_physics = true; } @@ -2716,6 +2727,6 @@ void PhysicalBone::_stop_physics_simulation() { PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), 0); PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), 0); PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, ""); - parent_skeleton->set_bone_ignore_animation(bone_id, false); + parent_skeleton->set_bone_global_pose_override(bone_id, Transform(), 0.0, false); _internal_simulate_physics = false; } diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 0e2e614717..0967cb9cd5 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -315,6 +315,7 @@ private: Ref<KinematicCollision> _get_slide_collision(int p_bounce); protected: + void _notification(int p_what); static void _bind_methods(); public: diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index e192e040f2..ead1e69f90 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -36,6 +36,34 @@ #include "scene/3d/physics_body.h" #include "scene/resources/surface_tool.h" +void SkinReference::_skin_changed() { + if (skeleton_node) { + skeleton_node->_make_dirty(); + } +} + +void SkinReference::_bind_methods() { + ClassDB::bind_method(D_METHOD("_skin_changed"), &SkinReference::_skin_changed); + ClassDB::bind_method(D_METHOD("get_skeleton"), &SkinReference::get_skeleton); + ClassDB::bind_method(D_METHOD("get_skin"), &SkinReference::get_skin); +} + +RID SkinReference::get_skeleton() const { + return skeleton; +} + +Ref<Skin> SkinReference::get_skin() const { + return skin; +} + +SkinReference::~SkinReference() { + if (skeleton_node) { + skeleton_node->skin_bindings.erase(this); + } + + VS::get_singleton()->free(skeleton); +} + bool Skeleton::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; @@ -196,110 +224,77 @@ void Skeleton::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_WORLD: { - - VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform()); - - } break; - case NOTIFICATION_EXIT_WORLD: { - - } break; - case NOTIFICATION_TRANSFORM_CHANGED: { - - VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform()); - } break; case NOTIFICATION_UPDATE_SKELETON: { VisualServer *vs = VisualServer::get_singleton(); Bone *bonesptr = bones.ptrw(); int len = bones.size(); - vs->skeleton_allocate(skeleton, len); // if same size, nothing really happens - _update_process_order(); const int *order = process_order.ptr(); - // pose changed, rebuild cache of inverses - if (rest_global_inverse_dirty) { - - // calculate global rests and invert them - for (int i = 0; i < len; i++) { - Bone &b = bonesptr[order[i]]; - if (b.parent >= 0) - b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest; - else - b.rest_global_inverse = b.rest; - } - for (int i = 0; i < len; i++) { - Bone &b = bonesptr[order[i]]; - b.rest_global_inverse.affine_invert(); - } - - rest_global_inverse_dirty = false; - } - for (int i = 0; i < len; i++) { Bone &b = bonesptr[order[i]]; - if (b.disable_rest) { - if (b.enabled) { + if (b.global_pose_override_amount >= 0.999) { + b.pose_global = b.global_pose_override; + } else { + if (b.disable_rest) { + if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { + Transform pose = b.pose; + if (b.parent >= 0) { - pose = b.custom_pose * pose; - } + b.pose_global = bonesptr[b.parent].pose_global * pose; + } else { - if (b.parent >= 0) { - - b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global = pose; + } } else { - b.pose_global = pose; - } - } else { - - if (b.parent >= 0) { + if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - } else { + b.pose_global = bonesptr[b.parent].pose_global; + } else { - b.pose_global = Transform(); + b.pose_global = Transform(); + } } - } - } else { - if (b.enabled) { + } else { + if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { + Transform pose = b.pose; - pose = b.custom_pose * pose; - } + if (b.parent >= 0) { - if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); + } else { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); + b.pose_global = b.rest * pose; + } } else { - b.pose_global = b.rest * pose; - } - } else { + if (b.parent >= 0) { - if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + } else { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - } else { - - b.pose_global = b.rest; + b.pose_global = b.rest; + } } } + + if (b.global_pose_override_amount >= CMP_EPSILON) { + b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); + } } - b.transform_final = b.pose_global * b.rest_global_inverse; - vs->skeleton_bone_set_transform(skeleton, order[i], b.transform_final); + if (b.global_pose_override_reset) { + b.global_pose_override_amount = 0.0; + } for (List<uint32_t>::Element *E = b.nodes_bound.front(); E; E = E->next()) { @@ -311,28 +306,37 @@ void Skeleton::_notification(int p_what) { } } + //update skins + for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + + const Skin *skin = E->get()->skin.operator->(); + RID skeleton = E->get()->skeleton; + uint32_t bind_count = skin->get_bind_count(); + + if (E->get()->bind_count != bind_count) { + VS::get_singleton()->skeleton_allocate(skeleton, bind_count); + E->get()->bind_count = bind_count; + } + + for (uint32_t i = 0; i < bind_count; i++) { + uint32_t bone_index = skin->get_bind_bone(i); + ERR_CONTINUE(bone_index >= (uint32_t)len); + vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i)); + } + } + dirty = false; } break; } } -Transform Skeleton::get_bone_transform(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); - if (dirty) - const_cast<Skeleton *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); - return bones[p_bone].pose_global * bones[p_bone].rest_global_inverse; -} - -void Skeleton::set_bone_global_pose(int p_bone, const Transform &p_pose) { +void Skeleton::set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent) { ERR_FAIL_INDEX(p_bone, bones.size()); - if (bones[p_bone].parent == -1) { - - set_bone_pose(p_bone, bones[p_bone].rest_global_inverse * p_pose); //fast - } else { - - set_bone_pose(p_bone, bones[p_bone].rest.affine_inverse() * (get_bone_global_pose(bones[p_bone].parent).affine_inverse() * p_pose)); //slow - } + bones.write[p_bone].global_pose_override_amount = p_amount; + bones.write[p_bone].global_pose_override = p_pose; + bones.write[p_bone].global_pose_override_reset = !p_persistent; + _make_dirty(); } Transform Skeleton::get_bone_global_pose(int p_bone) const { @@ -343,11 +347,6 @@ Transform Skeleton::get_bone_global_pose(int p_bone) const { return bones[p_bone].pose_global; } -RID Skeleton::get_skeleton() const { - - return skeleton; -} - // skeleton creation api void Skeleton::add_bone(const String &p_name) { @@ -362,8 +361,6 @@ void Skeleton::add_bone(const String &p_name) { b.name = p_name; bones.push_back(b); process_order_dirty = true; - - rest_global_inverse_dirty = true; _make_dirty(); update_gizmo(); } @@ -408,7 +405,6 @@ void Skeleton::set_bone_parent(int p_bone, int p_parent) { ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); bones.write[p_bone].parent = p_parent; - rest_global_inverse_dirty = true; process_order_dirty = true; _make_dirty(); } @@ -426,23 +422,11 @@ void Skeleton::unparent_bone_and_rest(int p_bone) { } bones.write[p_bone].parent = -1; - bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing process_order_dirty = true; _make_dirty(); } -void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) { - ERR_FAIL_INDEX(p_bone, bones.size()); - bones.write[p_bone].ignore_animation = p_ignore; -} - -bool Skeleton::is_bone_ignore_animation(int p_bone) const { - - ERR_FAIL_INDEX_V(p_bone, bones.size(), false); - return bones[p_bone].ignore_animation; -} - void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) { ERR_FAIL_INDEX(p_bone, bones.size()); @@ -467,7 +451,6 @@ void Skeleton::set_bone_rest(int p_bone, const Transform &p_rest) { ERR_FAIL_INDEX(p_bone, bones.size()); bones.write[p_bone].rest = p_rest; - rest_global_inverse_dirty = true; _make_dirty(); } Transform Skeleton::get_bone_rest(int p_bone) const { @@ -482,7 +465,6 @@ void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) { ERR_FAIL_INDEX(p_bone, bones.size()); bones.write[p_bone].enabled = p_enabled; - rest_global_inverse_dirty = true; _make_dirty(); } bool Skeleton::is_bone_enabled(int p_bone) const { @@ -529,7 +511,6 @@ void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) void Skeleton::clear_bones() { bones.clear(); - rest_global_inverse_dirty = true; process_order_dirty = true; _make_dirty(); @@ -552,23 +533,6 @@ Transform Skeleton::get_bone_pose(int p_bone) const { return bones[p_bone].pose; } -void Skeleton::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) { - - ERR_FAIL_INDEX(p_bone, bones.size()); - //ERR_FAIL_COND( !is_inside_scene() ); - - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform()); - bones.write[p_bone].custom_pose = p_custom_pose; - - _make_dirty(); -} - -Transform Skeleton::get_bone_custom_pose(int p_bone) const { - - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); - return bones[p_bone].custom_pose; -} - void Skeleton::_make_dirty() { if (dirty) @@ -747,14 +711,66 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) { #endif // _3D_DISABLED -void Skeleton::set_use_bones_in_world_transform(bool p_enable) { - use_bones_in_world_transform = p_enable; - if (is_inside_tree()) { - VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform()); - } +void Skeleton::_skin_changed() { + _make_dirty(); } -bool Skeleton::is_using_bones_in_world_transform() const { - return use_bones_in_world_transform; + +Ref<SkinReference> Skeleton::register_skin(const Ref<Skin> &p_skin) { + + for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + if (E->get()->skin == p_skin) { + return Ref<SkinReference>(E->get()); + } + } + + Ref<Skin> skin = p_skin; + + if (skin.is_null()) { + //need to create one from existing code, this is for compatibility only + //when skeletons did not support skins. It is also used by gizmo + //to display the skeleton. + + skin.instance(); + skin->set_bind_count(bones.size()); + _update_process_order(); //just in case + + // pose changed, rebuild cache of inverses + const Bone *bonesptr = bones.ptr(); + int len = bones.size(); + const int *order = process_order.ptr(); + + // calculate global rests and invert them + for (int i = 0; i < len; i++) { + const Bone &b = bonesptr[order[i]]; + if (b.parent >= 0) { + skin->set_bind_pose(order[i], skin->get_bind_pose(b.parent) * b.rest); + } else { + skin->set_bind_pose(order[i], b.rest); + } + } + + for (int i = 0; i < len; i++) { + //the inverse is what is actually required + skin->set_bind_bone(i, i); + skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse()); + } + } + + ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>()); + + Ref<SkinReference> skin_ref; + skin_ref.instance(); + + skin_ref->skeleton_node = this; + skin_ref->bind_count = 0; + skin_ref->skeleton = VisualServer::get_singleton()->skeleton_create(); + skin_ref->skeleton_node = this; + skin_ref->skin = skin; + + skin_bindings.insert(skin_ref.operator->()); + + skin->connect("changed", skin_ref.operator->(), "_skin_changed"); + return skin_ref; } void Skeleton::_bind_methods() { @@ -773,6 +789,8 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton::set_bone_rest); + ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton::register_skin); + ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton::localize_rests); ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton::set_bone_disable_rest); @@ -787,17 +805,9 @@ void Skeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton::get_bone_pose); ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton::set_bone_pose); - ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton::set_bone_global_pose); + ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton::set_bone_global_pose_override, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton::get_bone_global_pose); - ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton::get_bone_custom_pose); - ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton::set_bone_custom_pose); - - ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform); - - ClassDB::bind_method(D_METHOD("set_use_bones_in_world_transform", "enable"), &Skeleton::set_use_bones_in_world_transform); - ClassDB::bind_method(D_METHOD("is_using_bones_in_world_transform"), &Skeleton::is_using_bones_in_world_transform); - #ifndef _3D_DISABLED ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation); @@ -807,22 +817,19 @@ void Skeleton::_bind_methods() { #endif // _3D_DISABLED - ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones_in_world_transform"), "set_use_bones_in_world_transform", "is_using_bones_in_world_transform"); BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } Skeleton::Skeleton() { - rest_global_inverse_dirty = true; dirty = false; process_order_dirty = true; - skeleton = VisualServer::get_singleton()->skeleton_create(); - set_notify_transform(true); - use_bones_in_world_transform = false; } Skeleton::~Skeleton() { - VisualServer::get_singleton()->free(skeleton); + + //some skins may remain bound + for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { + E->get()->skeleton_node = nullptr; + } } diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h index 5b55dffbc8..f20c550055 100644 --- a/scene/3d/skeleton.h +++ b/scene/3d/skeleton.h @@ -33,6 +33,7 @@ #include "core/rid.h" #include "scene/3d/spatial.h" +#include "scene/resources/skin.h" #ifndef _3D_DISABLED typedef int BoneId; @@ -40,10 +41,38 @@ typedef int BoneId; class PhysicalBone; #endif // _3D_DISABLED +class Skeleton; + +class SkinReference : public Reference { + GDCLASS(SkinReference, Reference) + friend class Skeleton; + + Skeleton *skeleton_node; + RID skeleton; + Ref<Skin> skin; + uint32_t bind_count = 0; + void _skin_changed(); + +protected: + static void _bind_methods(); + +public: + RID get_skeleton() const; + Ref<Skin> get_skin() const; + ~SkinReference(); +}; + class Skeleton : public Spatial { GDCLASS(Skeleton, Spatial); +private: + friend class SkinReference; + + Set<SkinReference *> skin_bindings; + + void _skin_changed(); + struct Bone { String name; @@ -52,19 +81,15 @@ class Skeleton : public Spatial { int parent; int sort_index; //used for re-sorting process order - bool ignore_animation; - bool disable_rest; Transform rest; - Transform rest_global_inverse; Transform pose; Transform pose_global; - bool custom_pose_enable; - Transform custom_pose; - - Transform transform_final; + float global_pose_override_amount; + bool global_pose_override_reset; + Transform global_pose_override; #ifndef _3D_DISABLED PhysicalBone *physical_bone; @@ -76,9 +101,9 @@ class Skeleton : public Spatial { Bone() { parent = -1; enabled = true; - ignore_animation = false; - custom_pose_enable = false; disable_rest = false; + global_pose_override_amount = 0; + global_pose_override_reset = false; #ifndef _3D_DISABLED physical_bone = NULL; cache_parent_physical_bone = NULL; @@ -86,17 +111,12 @@ class Skeleton : public Spatial { } }; - bool rest_global_inverse_dirty; - Vector<Bone> bones; Vector<int> process_order; bool process_order_dirty; - RID skeleton; - void _make_dirty(); bool dirty; - bool use_bones_in_world_transform; // bind helpers Array _get_bound_child_nodes_to_bone(int p_bone) const { @@ -127,8 +147,6 @@ public: NOTIFICATION_UPDATE_SKELETON = 50 }; - RID get_skeleton() const; - // skeleton creation api void add_bone(const String &p_name); int find_bone(const String &p_name) const; @@ -141,9 +159,6 @@ public: void unparent_bone_and_rest(int p_bone); - void set_bone_ignore_animation(int p_bone, bool p_ignore); - bool is_bone_ignore_animation(int p_bone) const; - void set_bone_disable_rest(int p_bone, bool p_disable); bool is_bone_rest_disabled(int p_bone) const; @@ -151,10 +166,9 @@ public: void set_bone_rest(int p_bone, const Transform &p_rest); Transform get_bone_rest(int p_bone) const; - Transform get_bone_transform(int p_bone) const; Transform get_bone_global_pose(int p_bone) const; - void set_bone_global_pose(int p_bone, const Transform &p_pose); + void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false); void set_bone_enabled(int p_bone, bool p_enabled); bool is_bone_enabled(int p_bone) const; @@ -170,14 +184,10 @@ public: void set_bone_pose(int p_bone, const Transform &p_pose); Transform get_bone_pose(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform &p_custom_pose); - Transform get_bone_custom_pose(int p_bone) const; - void localize_rests(); // used for loaders and tools int get_process_order(int p_idx); - void set_use_bones_in_world_transform(bool p_enable); - bool is_using_bones_in_world_transform() const; + Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); #ifndef _3D_DISABLED // Physical bone API diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index fc5523633d..a9dacc442c 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -286,6 +286,18 @@ SpriteBase3D::AlphaCutMode SpriteBase3D::get_alpha_cut_mode() const { return alpha_cut; } +void SpriteBase3D::set_billboard_mode(SpatialMaterial::BillboardMode p_mode) { + + ERR_FAIL_INDEX(p_mode, 3); + billboard_mode = p_mode; + _queue_update(); +} + +SpatialMaterial::BillboardMode SpriteBase3D::get_billboard_mode() const { + + return billboard_mode; +} + void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_centered", "centered"), &SpriteBase3D::set_centered); @@ -318,6 +330,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_cut_mode", "mode"), &SpriteBase3D::set_alpha_cut_mode); ClassDB::bind_method(D_METHOD("get_alpha_cut_mode"), &SpriteBase3D::get_alpha_cut_mode); + ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode); + ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode); + ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); @@ -333,6 +348,7 @@ void SpriteBase3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001"), "set_pixel_size", "get_pixel_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "axis", PROPERTY_HINT_ENUM, "X-Axis,Y-Axis,Z-Axis"), "set_axis", "get_axis"); ADD_GROUP("Flags", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard"), "set_billboard_mode", "get_billboard_mode"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_draw_flag", "get_draw_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "shaded"), "set_draw_flag", "get_draw_flag", FLAG_SHADED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "double_sided"), "set_draw_flag", "get_draw_flag", FLAG_DOUBLE_SIDED); @@ -361,6 +377,7 @@ SpriteBase3D::SpriteBase3D() { flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED; alpha_cut = ALPHA_CUT_DISABLED; + billboard_mode = SpatialMaterial::BILLBOARD_DISABLED; axis = Vector3::AXIS_Z; pixel_size = 0.01; modulate = Color(1, 1, 1, 1); @@ -463,7 +480,7 @@ void Sprite3D::_draw() { tangent = Plane(1, 0, 0, 1); } - RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS); + RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == SpatialMaterial::BILLBOARD_ENABLED, get_billboard_mode() == SpatialMaterial::BILLBOARD_FIXED_Y); VS::get_singleton()->immediate_set_material(immediate, mat); VS::get_singleton()->immediate_begin(immediate, VS::PRIMITIVE_TRIANGLE_FAN, texture->get_rid()); @@ -788,7 +805,7 @@ void AnimatedSprite3D::_draw() { tangent = Plane(1, 0, 0, -1); } - RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS); + RID mat = SpatialMaterial::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == SpatialMaterial::BILLBOARD_ENABLED, get_billboard_mode() == SpatialMaterial::BILLBOARD_FIXED_Y); VS::get_singleton()->immediate_set_material(immediate, mat); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 97a426b5b9..065931de84 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -80,6 +80,7 @@ private: bool flags[FLAG_MAX]; AlphaCutMode alpha_cut; + SpatialMaterial::BillboardMode billboard_mode; bool pending_update; void _im_update(); @@ -130,6 +131,8 @@ public: void set_alpha_cut_mode(AlphaCutMode p_mode); AlphaCutMode get_alpha_cut_mode() const; + void set_billboard_mode(SpatialMaterial::BillboardMode p_mode); + SpatialMaterial::BillboardMode get_billboard_mode() const; virtual Rect2 get_item_rect() const = 0; diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 051f832882..728c23fbaa 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -256,7 +256,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { Skeleton *sk = Object::cast_to<Skeleton>(child); bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0)); - if (bone_idx == -1 || sk->is_bone_ignore_animation(bone_idx)) { + if (bone_idx == -1) { continue; } diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index bb7c400cfe..eb152bc41e 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -622,7 +622,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) { Skeleton *sk = Object::cast_to<Skeleton>(spatial); int bone_idx = sk->find_bone(path.get_subname(0)); - if (bone_idx != -1 && !sk->is_bone_ignore_animation(bone_idx)) { + if (bone_idx != -1) { track_xform->skeleton = sk; track_xform->bone_idx = bone_idx; diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 8f6d53c21c..ba5936562a 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -820,11 +820,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) { t.value = t.object->get_indexed(t.subpath); t.value.zero(); - if (t.skeleton) { - t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx); - } else { - t.skip = false; - } + t.skip = false; } /* STEP 2 PROCESS ANIMATIONS */ diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 7a1b10792b..4ec22cf3df 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -320,7 +320,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis; } - p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); + p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0); if (!ci->children.empty()) ci = &ci->children.write[0]; diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 2609924f33..1f9793190d 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -191,7 +191,7 @@ void Tween::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { // Are we processing during 'regular' time? if (tween_process_mode == TWEEN_PROCESS_IDLE) - // Do nothing since we whould only process during idle time + // Do nothing since we would only process during idle time break; // Should we update? @@ -783,10 +783,12 @@ float Tween::get_speed_scale() const { } bool Tween::start() { + + ERR_FAIL_COND_V_MSG(!is_inside_tree(), false, "Tween was not added to the SceneTree!"); + // Are there any pending updates? if (pending_update != 0) { // Start the tweens after deferring - call_deferred("start"); return true; } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 65e9cccd05..6b3e89af6c 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -39,158 +39,191 @@ Size2 Button::get_minimum_size() const { if (clip_text) minsize.width = 0; - Ref<Texture> _icon; - if (icon.is_null() && has_icon("icon")) - _icon = Control::get_icon("icon"); - else - _icon = icon; - - if (!_icon.is_null()) { - - minsize.height = MAX(minsize.height, _icon->get_height()); - minsize.width += _icon->get_width(); - if (xl_text != "") - minsize.width += get_constant("hseparation"); + if (!expand_icon) { + Ref<Texture> _icon; + if (icon.is_null() && has_icon("icon")) + _icon = Control::get_icon("icon"); + else + _icon = icon; + + if (!_icon.is_null()) { + + minsize.height = MAX(minsize.height, _icon->get_height()); + minsize.width += _icon->get_width(); + if (xl_text != "") + minsize.width += get_constant("hseparation"); + } } return get_stylebox("normal")->get_minimum_size() + minsize; } void Button::_set_internal_margin(Margin p_margin, float p_value) { + _internal_margin[p_margin] = p_value; } void Button::_notification(int p_what) { - if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { + switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: { - xl_text = tr(text); - minimum_size_changed(); - update(); - } + xl_text = tr(text); + minimum_size_changed(); + update(); + } break; + case NOTIFICATION_DRAW: { - if (p_what == NOTIFICATION_DRAW) { + RID ci = get_canvas_item(); + Size2 size = get_size(); + Color color; + Color color_icon(1, 1, 1, 1); - RID ci = get_canvas_item(); - Size2 size = get_size(); - Color color; - Color color_icon(1, 1, 1, 1); + Ref<StyleBox> style = get_stylebox("normal"); - Ref<StyleBox> style = get_stylebox("normal"); + switch (get_draw_mode()) { + case DRAW_NORMAL: { - switch (get_draw_mode()) { - - case DRAW_NORMAL: { + style = get_stylebox("normal"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color"); + if (has_color("icon_color_normal")) + color_icon = get_color("icon_color_normal"); + } break; + case DRAW_HOVER_PRESSED: { + + if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { + style = get_stylebox("hover_pressed"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + if (has_color("font_color_hover_pressed")) + color = get_color("font_color_hover_pressed"); + else + color = get_color("font_color"); + if (has_color("icon_color_hover_pressed")) + color_icon = get_color("icon_color_hover_pressed"); + + break; + } + FALLTHROUGH; + } + case DRAW_PRESSED: { - style = get_stylebox("normal"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color"); - if (has_color("icon_color_normal")) - color_icon = get_color("icon_color_normal"); - } break; - case DRAW_HOVER_PRESSED: { - if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { - style = get_stylebox("hover_pressed"); + style = get_stylebox("pressed"); if (!flat) style->draw(ci, Rect2(Point2(0, 0), size)); - if (has_color("font_color_hover_pressed")) - color = get_color("font_color_hover_pressed"); + if (has_color("font_color_pressed")) + color = get_color("font_color_pressed"); else color = get_color("font_color"); - if (has_color("icon_color_hover_pressed")) - color_icon = get_color("icon_color_hover_pressed"); + if (has_color("icon_color_pressed")) + color_icon = get_color("icon_color_pressed"); - break; - } - FALLTHROUGH; + } break; + case DRAW_HOVER: { + + style = get_stylebox("hover"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color_hover"); + if (has_color("icon_color_hover")) + color_icon = get_color("icon_color_hover"); + + } break; + case DRAW_DISABLED: { + + style = get_stylebox("disabled"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color_disabled"); + if (has_color("icon_color_disabled")) + color_icon = get_color("icon_color_disabled"); + + } break; } - case DRAW_PRESSED: { - - style = get_stylebox("pressed"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - if (has_color("font_color_pressed")) - color = get_color("font_color_pressed"); - else - color = get_color("font_color"); - if (has_color("icon_color_pressed")) - color_icon = get_color("icon_color_pressed"); - - } break; - case DRAW_HOVER: { - - style = get_stylebox("hover"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color_hover"); - if (has_color("icon_color_hover")) - color_icon = get_color("icon_color_hover"); - - } break; - case DRAW_DISABLED: { - - style = get_stylebox("disabled"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color_disabled"); - if (has_color("icon_color_disabled")) - color_icon = get_color("icon_color_disabled"); - - } break; - } - if (has_focus()) { + if (has_focus()) { - Ref<StyleBox> style2 = get_stylebox("focus"); - style2->draw(ci, Rect2(Point2(), size)); - } + Ref<StyleBox> style2 = get_stylebox("focus"); + style2->draw(ci, Rect2(Point2(), size)); + } - Ref<Font> font = get_font("font"); - Ref<Texture> _icon; - if (icon.is_null() && has_icon("icon")) - _icon = Control::get_icon("icon"); - else - _icon = icon; + Ref<Font> font = get_font("font"); + Ref<Texture> _icon; + if (icon.is_null() && has_icon("icon")) + _icon = Control::get_icon("icon"); + else + _icon = icon; + + Rect2 icon_region = Rect2(); + if (!_icon.is_null()) { - Point2 icon_ofs = (!_icon.is_null()) ? Point2(_icon->get_width() + get_constant("hseparation"), 0) : Point2(); - int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; - Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; - - switch (align) { - case ALIGN_LEFT: { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); - text_ofs.y += style->get_offset().y; - } break; - case ALIGN_CENTER: { - if (text_ofs.x < 0) - text_ofs.x = 0; - text_ofs += icon_ofs; - text_ofs += style->get_offset(); - } break; - case ALIGN_RIGHT: { - if (_internal_margin[MARGIN_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation"); + int valign = size.height - style->get_minimum_size().y; + if (is_disabled()) { + color_icon.a = 0.4; + } + + float icon_ofs_region = 0; + if (_internal_margin[MARGIN_LEFT] > 0) { + icon_ofs_region = _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); + } + + if (expand_icon) { + Size2 _size = get_size() - style->get_offset() * 2; + _size.width -= get_constant("hseparation") + icon_ofs_region; + if (!clip_text) + _size.width -= get_font("font")->get_string_size(xl_text).width; + float icon_width = icon->get_width() * _size.height / icon->get_height(); + float icon_height = _size.height; + + if (icon_width > _size.width) { + icon_width = _size.width; + icon_height = icon->get_height() * icon_width / icon->get_width(); + } + + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); } else { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), icon->get_size()); } - text_ofs.y += style->get_offset().y; - } break; - } + } - text_ofs.y += font->get_ascent(); - font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); - if (!_icon.is_null()) { + Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_constant("hseparation"), 0) : Point2(); + int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; + Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; + + switch (align) { + case ALIGN_LEFT: { + if (_internal_margin[MARGIN_LEFT] > 0) { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); + } else { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + } + text_ofs.y += style->get_offset().y; + } break; + case ALIGN_CENTER: { + if (text_ofs.x < 0) + text_ofs.x = 0; + text_ofs += icon_ofs; + text_ofs += style->get_offset(); + } break; + case ALIGN_RIGHT: { + if (_internal_margin[MARGIN_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation"); + } else { + text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + } + text_ofs.y += style->get_offset().y; + } break; + } + + text_ofs.y += font->get_ascent(); + font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); - int valign = size.height - style->get_minimum_size().y; - if (is_disabled()) - color_icon.a = 0.4; - if (_internal_margin[MARGIN_LEFT] > 0) { - _icon->draw(ci, style->get_offset() + Point2(_internal_margin[MARGIN_LEFT] + get_constant("hseparation"), Math::floor((valign - _icon->get_height()) / 2.0)), color_icon); - } else { - _icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon); + if (!_icon.is_null() && icon_region.size.width > 0) { + draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), icon->get_size()), color_icon); } - } + } break; } } @@ -224,6 +257,18 @@ Ref<Texture> Button::get_icon() const { return icon; } +void Button::set_expand_icon(bool p_expand_icon) { + + expand_icon = p_expand_icon; + update(); + minimum_size_changed(); +} + +bool Button::is_expand_icon() const { + + return expand_icon; +} + void Button::set_flat(bool p_flat) { flat = p_flat; @@ -265,6 +310,8 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); + ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); + ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text); @@ -281,12 +328,14 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); } Button::Button(const String &p_text) { flat = false; clip_text = false; + expand_icon = false; set_mouse_filter(MOUSE_FILTER_STOP); set_text(p_text); align = ALIGN_CENTER; diff --git a/scene/gui/button.h b/scene/gui/button.h index 370809060e..1fff2cfda7 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -49,6 +49,7 @@ private: String text; String xl_text; Ref<Texture> icon; + bool expand_icon; bool clip_text; TextAlign align; float _internal_margin[4]; @@ -59,8 +60,6 @@ protected: static void _bind_methods(); public: - // - virtual Size2 get_minimum_size() const; void set_text(const String &p_text); @@ -69,6 +68,9 @@ public: void set_icon(const Ref<Texture> &p_icon); Ref<Texture> get_icon() const; + void set_expand_icon(bool p_expand_icon); + bool is_expand_icon() const; + void set_flat(bool p_flat); bool is_flat() const; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 1d529f4e72..6dd9e401f6 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -964,6 +964,7 @@ void ColorPickerButton::_update_picker() { popup->connect("popup_hide", this, "set_pressed", varray(false)); picker->set_pick_color(color); picker->set_edit_alpha(edit_alpha); + emit_signal("picker_created"); } } @@ -980,6 +981,7 @@ void ColorPickerButton::_bind_methods() { ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("popup_closed")); + ADD_SIGNAL(MethodInfo("picker_created")); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 034e9266f6..d39f017cad 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -818,7 +818,7 @@ Size2 Control::get_minimum_size() const { Ref<Texture> Control::get_icon(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { const Ref<Texture> *tex = data.icon_override.getptr(p_name); if (tex) @@ -860,7 +860,7 @@ Ref<Texture> Control::get_icon(const StringName &p_name, const StringName &p_typ } Ref<Shader> Control::get_shader(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { const Ref<Shader> *sdr = data.shader_override.getptr(p_name); if (sdr) @@ -903,7 +903,7 @@ Ref<Shader> Control::get_shader(const StringName &p_name, const StringName &p_ty Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { const Ref<StyleBox> *style = data.style_override.getptr(p_name); if (style) return *style; @@ -949,7 +949,7 @@ Ref<StyleBox> Control::get_stylebox(const StringName &p_name, const StringName & } Ref<Font> Control::get_font(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { const Ref<Font> *font = data.font_override.getptr(p_name); if (font) return *font; @@ -986,7 +986,7 @@ Ref<Font> Control::get_font(const StringName &p_name, const StringName &p_type) } Color Control::get_color(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { const Color *color = data.color_override.getptr(p_name); if (color) return *color; @@ -1026,7 +1026,7 @@ Color Control::get_color(const StringName &p_name, const StringName &p_type) con int Control::get_constant(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { const int *constant = data.constant_override.getptr(p_name); if (constant) return *constant; @@ -1102,7 +1102,7 @@ bool Control::has_constant_override(const StringName &p_name) const { bool Control::has_icon(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { if (has_icon_override(p_name)) return true; } @@ -1141,7 +1141,7 @@ bool Control::has_icon(const StringName &p_name, const StringName &p_type) const bool Control::has_shader(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { if (has_shader_override(p_name)) return true; } @@ -1179,7 +1179,7 @@ bool Control::has_shader(const StringName &p_name, const StringName &p_type) con } bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { if (has_stylebox_override(p_name)) return true; } @@ -1217,7 +1217,7 @@ bool Control::has_stylebox(const StringName &p_name, const StringName &p_type) c } bool Control::has_font(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { if (has_font_override(p_name)) return true; } @@ -1256,7 +1256,7 @@ bool Control::has_font(const StringName &p_name, const StringName &p_type) const bool Control::has_color(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { if (has_color_override(p_name)) return true; } @@ -1295,7 +1295,7 @@ bool Control::has_color(const StringName &p_name, const StringName &p_type) cons bool Control::has_constant(const StringName &p_name, const StringName &p_type) const { - if (p_type == StringName() || p_type == "") { + if (p_type == StringName() || p_type == get_class_name()) { if (has_constant_override(p_name)) return true; } @@ -2149,9 +2149,7 @@ bool Control::has_focus() const { void Control::grab_focus() { - if (!is_inside_tree()) { - ERR_FAIL_COND(!is_inside_tree()); - } + ERR_FAIL_COND(!is_inside_tree()); if (data.focus_mode == FOCUS_NONE) { WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus."); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 59bbdad97a..9ed1d2bf45 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -35,6 +35,7 @@ #ifdef TOOLS_ENABLED #include "editor/editor_node.h" +#include "scene/main/viewport.h" // Only used to check for more modals when dimming the editor. #endif // WindowDialog @@ -59,7 +60,7 @@ void WindowDialog::_fix_size() { float left = 0; float bottom = 0; float right = 0; - // Check validity, because the theme could contain a different type of StyleBox + // Check validity, because the theme could contain a different type of StyleBox. if (panel->get_class() == "StyleBoxTexture") { Ref<StyleBoxTexture> panel_texture = Object::cast_to<StyleBoxTexture>(*panel); top = panel_texture->get_expand_margin_size(MARGIN_TOP); @@ -242,7 +243,7 @@ void WindowDialog::_notification(int p_what) { } break; case NOTIFICATION_POPUP_HIDE: { - if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton()) + if (get_tree() && Engine::get_singleton()->is_editor_hint() && EditorNode::get_singleton() && !get_viewport()->gui_has_modal_stack()) EditorNode::get_singleton()->dim_editor(false); } break; #endif diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 75f5f79873..09ef6f26bf 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -241,9 +241,13 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) { float newofs = CLAMP(x / float(total_w), 0, 1); - //Snap to nearest point if holding shift - if (mm->get_shift()) { - float snap_threshold = 0.03; + // Snap to "round" coordinates if holding Ctrl. + // Be more precise if holding Shift as well + if (mm->get_control()) { + newofs = Math::stepify(newofs, mm->get_shift() ? 0.025 : 0.1); + } else if (mm->get_shift()) { + // Snap to nearest point if holding just Shift + const float snap_threshold = 0.03; float smallest_ofs = snap_threshold; bool found = false; int nearest_point = 0; diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index fdffb26cb5..7827c66841 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -276,6 +276,11 @@ void GraphEdit::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { port_grab_distance_horizontal = get_constant("port_grab_distance_horizontal"); port_grab_distance_vertical = get_constant("port_grab_distance_vertical"); + + zoom_minus->set_icon(get_icon("minus")); + zoom_reset->set_icon(get_icon("reset")); + zoom_plus->set_icon(get_icon("more")); + snap_button->set_icon(get_icon("snap")); } if (p_what == NOTIFICATION_READY) { Size2 hmin = h_scroll->get_combined_minimum_size(); @@ -290,11 +295,6 @@ void GraphEdit::_notification(int p_what) { h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); h_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -hmin.height); h_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); - - zoom_minus->set_icon(get_icon("minus")); - zoom_reset->set_icon(get_icon("reset")); - zoom_plus->set_icon(get_icon("more")); - snap_button->set_icon(get_icon("snap")); } if (p_what == NOTIFICATION_DRAW) { diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index f7bef4ed39..5b2f8812d5 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -210,6 +210,7 @@ void GraphNode::_notification(int p_what) { int close_offset = get_constant("close_offset"); int close_h_offset = get_constant("close_h_offset"); Color close_color = get_color("close_color"); + Color resizer_color = get_color("resizer_color"); Ref<Font> title_font = get_font("title_font"); int title_offset = get_constant("title_offset"); int title_h_offset = get_constant("title_h_offset"); @@ -274,7 +275,7 @@ void GraphNode::_notification(int p_what) { } if (resizable) { - draw_texture(resizer, get_size() - resizer->get_size()); + draw_texture(resizer, get_size() - resizer->get_size(), resizer_color); } } break; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 4a763844f8..04e03f569e 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -87,7 +87,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } else { - if (b->is_doubleclick()) { + if (b->is_doubleclick() && selecting_enabled) { selection.enabled = true; selection.begin = 0; @@ -195,13 +195,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { unsigned int code = k->get_scancode(); - if (k->get_command()) { + if (k->get_command() && is_shortcut_keys_enabled()) { bool handled = true; switch (code) { - case (KEY_X): { // CUT + case (KEY_X): { // CUT. if (editable) { cut_text(); @@ -209,13 +209,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_C): { // COPY + case (KEY_C): { // COPY. copy_text(); } break; - case (KEY_V): { // PASTE + case (KEY_V): { // PASTE. if (editable) { @@ -224,7 +224,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_Z): { // undo / redo + case (KEY_Z): { // Undo/redo. if (editable) { if (k->get_shift()) { redo(); @@ -234,7 +234,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } break; - case (KEY_U): { // Delete from start to cursor + case (KEY_U): { // Delete from start to cursor. if (editable) { @@ -255,7 +255,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_Y): { // PASTE (Yank for unix users) + case (KEY_Y): { // PASTE (Yank for unix users). if (editable) { @@ -263,7 +263,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } break; - case (KEY_K): { // Delete from cursor_pos to end + case (KEY_K): { // Delete from cursor_pos to end. if (editable) { @@ -273,14 +273,15 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } break; - case (KEY_A): { //Select All + case (KEY_A): { // Select all. select(); + } break; #ifdef APPLE_STYLE_KEYS - case (KEY_LEFT): { // Go to start of text - like HOME key + case (KEY_LEFT): { // Go to start of text - like HOME key. set_cursor_position(0); } break; - case (KEY_RIGHT): { // Go to end of text - like END key + case (KEY_RIGHT): { // Go to end of text - like END key. set_cursor_position(text.length()); } break; #endif @@ -473,7 +474,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { int text_len = text.length(); if (cursor_pos == text_len) - break; // nothing to do + break; // Nothing to do. #ifdef APPLE_STYLE_KEYS if (k->get_alt()) { @@ -531,6 +532,16 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { set_cursor_position(text.length()); shift_selection_check_post(k->get_shift()); } break; + case KEY_MENU: { + if (context_menu_enabled) { + Point2 pos = Point2(get_cursor_pixel_pos(), (get_size().y + get_font("font")->get_height()) / 2); + menu->set_position(get_global_transform().xform(pos)); + menu->set_size(Vector2(1, 1)); + menu->set_scale(get_global_transform().get_scale()); + menu->popup(); + menu->grab_focus(); + } + } break; default: { @@ -730,7 +741,7 @@ void LineEdit::_notification(int p_what) { Color cursor_color = get_color("cursor_color"); const String &t = using_placeholder ? placeholder_translated : text; - // draw placeholder color + // Draw placeholder color. if (using_placeholder) font_color.a *= placeholder_alpha; @@ -745,24 +756,28 @@ void LineEdit::_notification(int p_what) { color_icon = get_color("clear_button_color"); } } - r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); + + float icon_width = MIN(r_icon->get_width(), r_icon->get_width() * (height - (style->get_margin(MARGIN_TOP) + style->get_margin(MARGIN_BOTTOM))) / r_icon->get_height()); + float icon_height = MIN(r_icon->get_height(), height - (style->get_margin(MARGIN_TOP) + style->get_margin(MARGIN_BOTTOM))); + Rect2 icon_region = Rect2(Point2(width - icon_width - style->get_margin(MARGIN_RIGHT), height / 2 - icon_height / 2), Size2(icon_width, icon_height)); + draw_texture_rect_region(r_icon, icon_region, Rect2(Point2(), r_icon->get_size()), color_icon); if (align == ALIGN_CENTER) { if (window_pos == 0) { - x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2); + x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - icon_width - style->get_margin(MARGIN_RIGHT) * 2) / 2); } } else { - x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT)); + x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - icon_width - style->get_margin(MARGIN_RIGHT)); } - ofs_max -= r_icon->get_width(); + ofs_max -= icon_width; } int caret_height = font->get_height() > y_area ? y_area : font->get_height(); FontDrawer drawer(font, Color(1, 1, 1)); while (true) { - //end of string, break! + // End of string, break. if (char_ofs >= t.length()) break; @@ -799,7 +814,7 @@ void LineEdit::_notification(int p_what) { CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; int char_width = font->get_char_size(cchar, next).width; - // end of widget, break! + // End of widget, break. if ((x_ofs + char_width) > ofs_max) break; @@ -854,7 +869,7 @@ void LineEdit::_notification(int p_what) { } } - if (char_ofs == cursor_pos && draw_caret) { //may be at the end + if (char_ofs == cursor_pos && draw_caret) { // May be at the end. if (ime_text.length() == 0) { #ifdef TOOLS_ENABLED VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); @@ -872,7 +887,9 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - if (!caret_blink_enabled) { + if (caret_blink_enabled) { + caret_blink_timer->start(); + } else { draw_caret = true; } @@ -886,6 +903,10 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_EXIT: { + if (caret_blink_enabled) { + caret_blink_timer->stop(); + } + OS::get_singleton()->set_ime_position(Point2()); OS::get_singleton()->set_ime_active(false); ime_text = ""; @@ -1037,7 +1058,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } pixel_ofs += char_w; - if (pixel_ofs > p_x) { //found what we look for + if (pixel_ofs > p_x) { // Found what we look for. break; } @@ -1047,17 +1068,67 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { set_cursor_position(ofs); } +int LineEdit::get_cursor_pixel_pos() { + + Ref<Font> font = get_font("font"); + int ofs = window_pos; + Ref<StyleBox> style = get_stylebox("normal"); + int pixel_ofs = 0; + Size2 size = get_size(); + bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; + int r_icon_width = Control::get_icon("clear")->get_width(); + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + pixel_ofs = int(style->get_offset().x); + } break; + case ALIGN_CENTER: { + + if (window_pos != 0) + pixel_ofs = int(style->get_offset().x); + else + pixel_ofs = int(size.width - (cached_width)) / 2; + + if (display_clear_icon) + pixel_ofs -= int(r_icon_width / 2 + style->get_margin(MARGIN_RIGHT)); + } break; + case ALIGN_RIGHT: { + + pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width)); + + if (display_clear_icon) + pixel_ofs -= int(r_icon_width + style->get_margin(MARGIN_RIGHT)); + } break; + } + + while (ofs < cursor_pos) { + if (font != NULL) { + pixel_ofs += font->get_char_size(text[ofs]).width; + } + ofs++; + } + + return pixel_ofs; +} + bool LineEdit::cursor_get_blink_enabled() const { return caret_blink_enabled; } void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; - if (p_enabled) { - caret_blink_timer->start(); - } else { - caret_blink_timer->stop(); + + if (has_focus()) { + if (p_enabled) { + caret_blink_timer->start(); + } else { + caret_blink_timer->stop(); + } } + draw_caret = true; } @@ -1072,10 +1143,12 @@ void LineEdit::cursor_set_blink_speed(const float p_speed) { void LineEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { - caret_blink_timer->stop(); - caret_blink_timer->start(); draw_caret = true; - update(); + if (has_focus()) { + caret_blink_timer->stop(); + caret_blink_timer->start(); + update(); + } } } @@ -1198,15 +1271,18 @@ void LineEdit::set_cursor_position(int p_pos) { Ref<Font> font = get_font("font"); if (cursor_pos <= window_pos) { - /* Adjust window if cursor goes too much to the left */ + // Adjust window if cursor goes too much to the left. set_window_pos(MAX(0, cursor_pos - 1)); } else { - /* Adjust window if cursor goes too much to the right */ + // Adjust window if cursor goes too much to the right. int window_width = get_size().width - style->get_minimum_size().width; bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { Ref<Texture> r_icon = display_clear_icon ? Control::get_icon("clear") : right_icon; - window_width -= r_icon->get_width(); + + float icon_width = MIN(r_icon->get_width(), r_icon->get_width() * (get_size().height - (style->get_margin(MARGIN_TOP) + style->get_margin(MARGIN_BOTTOM))) / r_icon->get_height()); + + window_width -= icon_width; } if (window_width < 0) @@ -1220,10 +1296,10 @@ void LineEdit::set_cursor_position(int p_pos) { for (int i = cursor_pos; i >= window_pos; i--) { if (i >= text.length()) { - //do not do this, because if the cursor is at the end, its just fine that it takes no space - //accum_width = font->get_char_size(' ').width; //anything should do + // Do not do this, because if the cursor is at the end, its just fine that it takes no space. + // accum_width = font->get_char_size(' ').width; } else { - accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; //anything should do + accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; // Anything should do. } if (accum_width > window_width) break; @@ -1288,12 +1364,13 @@ Size2 LineEdit::get_minimum_size() const { Size2 min = style->get_minimum_size(); min.height += font->get_height(); - //minimum size of text + // Minimum size of text. int space_size = font->get_char_size(' ').x; int mstext = get_constant("minimum_spaces") * space_size; if (expand_to_text_length) { - mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact, and because cursor needs a bit more when at the end + // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. + mstext = MAX(mstext, font->get_string_size(text).x + space_size); } min.width += mstext; @@ -1301,8 +1378,6 @@ Size2 LineEdit::get_minimum_size() const { return min; } -/* selection */ - void LineEdit::deselect() { selection.begin = 0; @@ -1335,6 +1410,8 @@ int LineEdit::get_max_length() const { } void LineEdit::selection_fill_at_cursor() { + if (!selecting_enabled) + return; selection.begin = cursor_pos; selection.end = selection.cursor_start; @@ -1349,6 +1426,8 @@ void LineEdit::selection_fill_at_cursor() { } void LineEdit::select_all() { + if (!selecting_enabled) + return; if (!text.length()) return; @@ -1365,32 +1444,7 @@ void LineEdit::set_editable(bool p_editable) { return; editable = p_editable; - - // Reorganize context menu. - menu->clear(); - - if (editable) { - menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); - menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); - } - - if (editable) { - menu->add_separator(); - menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); - } - - menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C); - - if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V); - } - - menu->add_separator(); - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A); - - if (editable) { - menu->add_item(RTR("Clear"), MENU_CLEAR); - } + _generate_context_menu(); update(); } @@ -1413,8 +1467,8 @@ bool LineEdit::is_secret() const { void LineEdit::set_secret_character(const String &p_string) { - // An empty string as the secret character would crash the engine - // It also wouldn't make sense to use multiple characters as the secret character + // An empty string as the secret character would crash the engine. + // It also wouldn't make sense to use multiple characters as the secret character. ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)."); secret_character = p_string; @@ -1426,6 +1480,8 @@ String LineEdit::get_secret_character() const { } void LineEdit::select(int p_from, int p_to) { + if (!selecting_enabled) + return; if (p_from == 0 && p_to == 0) { deselect(); @@ -1533,6 +1589,29 @@ bool LineEdit::is_clear_button_enabled() const { return clear_button_enabled; } +void LineEdit::set_shortcut_keys_enabled(bool p_enabled) { + shortcut_keys_enabled = p_enabled; + + _generate_context_menu(); +} + +bool LineEdit::is_shortcut_keys_enabled() const { + return shortcut_keys_enabled; +} + +void LineEdit::set_selecting_enabled(bool p_enabled) { + selecting_enabled = p_enabled; + + if (!selecting_enabled) + deselect(); + + _generate_context_menu(); +} + +bool LineEdit::is_selecting_enabled() const { + return selecting_enabled; +} + void LineEdit::set_right_icon(const Ref<Texture> &p_icon) { if (right_icon == p_icon) { return; @@ -1541,8 +1620,11 @@ void LineEdit::set_right_icon(const Ref<Texture> &p_icon) { update(); } -void LineEdit::_text_changed() { +Ref<Texture> LineEdit::get_right_icon() { + return right_icon; +} +void LineEdit::_text_changed() { if (expand_to_text_length) minimum_size_changed(); @@ -1596,6 +1678,25 @@ void LineEdit::_create_undo_state() { undo_stack.push_back(op); } +void LineEdit::_generate_context_menu() { + // Reorganize context menu. + menu->clear(); + if (editable) + menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0); + menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0); + if (editable) + menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0); + menu->add_separator(); + if (is_selecting_enabled()) + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0); + if (editable) { + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0); + menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0); + } +} + void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed); @@ -1640,6 +1741,12 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled); ClassDB::bind_method(D_METHOD("set_clear_button_enabled", "enable"), &LineEdit::set_clear_button_enabled); ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled); + ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &LineEdit::set_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled); + ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled); + ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon); + ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text"))); @@ -1668,6 +1775,9 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); ADD_GROUP("Placeholder", "placeholder_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); @@ -1695,6 +1805,8 @@ LineEdit::LineEdit() { clear_button_enabled = false; clear_button_status.press_attempt = false; clear_button_status.pressing_inside = false; + shortcut_keys_enabled = true; + selecting_enabled = true; deselect(); set_focus_mode(FOCUS_ALL); @@ -1712,7 +1824,7 @@ LineEdit::LineEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - editable = false; // initialise to opposite first, so we get past the early-out in set_editable + editable = false; // Initialise to opposite first, so we get past the early-out in set_editable. set_editable(true); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 1d33f7d4ce..3424131dad 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -75,18 +75,22 @@ private: String ime_text; Point2 ime_selection; + bool selecting_enabled; + bool context_menu_enabled; PopupMenu *menu; int cursor_pos; int window_pos; - int max_length; // 0 for no maximum + int max_length; // 0 for no maximum. int cached_width; int cached_placeholder_width; bool clear_button_enabled; + bool shortcut_keys_enabled; + Ref<Texture> right_icon; struct Selection { @@ -118,6 +122,8 @@ private: void _clear_redo(); void _create_undo_state(); + void _generate_context_menu(); + Timer *caret_blink_timer; void _text_changed(); @@ -137,6 +143,7 @@ private: void set_window_pos(int p_pos); void set_cursor_at_pixel_pos(int p_x); + int get_cursor_pixel_pos(); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -216,7 +223,14 @@ public: void set_clear_button_enabled(bool p_enabled); bool is_clear_button_enabled() const; + void set_shortcut_keys_enabled(bool p_enabled); + bool is_shortcut_keys_enabled() const; + + void set_selecting_enabled(bool p_enabled); + bool is_selecting_enabled() const; + void set_right_icon(const Ref<Texture> &p_icon); + Ref<Texture> get_right_icon(); virtual bool is_text_field() const; LineEdit(); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index d1840e43a3..de8df4215d 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -116,10 +116,16 @@ void OptionButton::add_item(const String &p_label, int p_id) { void OptionButton::set_item_text(int p_idx, const String &p_text) { popup->set_item_text(p_idx, p_text); + + if (current == p_idx) + set_text(p_text); } void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { popup->set_item_icon(p_idx, p_icon); + + if (current == p_idx) + set_icon(p_icon); } void OptionButton::set_item_id(int p_idx, int p_id) { diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp new file mode 100644 index 0000000000..67fa85b832 --- /dev/null +++ b/scene/gui/rich_text_effect.cpp @@ -0,0 +1,122 @@ +/*************************************************************************/ +/* rich_text_effect.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "rich_text_effect.h" + +#include "core/script_language.h" + +void RichTextEffect::_bind_methods() { + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_process_custom_fx", PropertyInfo(Variant::OBJECT, "char_fx", PROPERTY_HINT_RESOURCE_TYPE, "CharFXTransform"))); +} + +Variant RichTextEffect::get_bbcode() const { + Variant r; + if (get_script_instance()) { + if (!get_script_instance()->get("bbcode", r)) { + String path = get_script_instance()->get_script()->get_path(); + r = path.get_file().get_basename(); + } + } + return r; +} + +bool RichTextEffect::_process_effect_impl(Ref<CharFXTransform> p_cfx) { + bool return_value = false; + if (get_script_instance()) { + Variant v = get_script_instance()->call("_process_custom_fx", p_cfx); + if (v.get_type() != Variant::BOOL) { + return_value = false; + } else { + return_value = (bool)v; + } + } + return return_value; +} + +RichTextEffect::RichTextEffect() { +} + +void CharFXTransform::_bind_methods() { + + ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index); + ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index); + + ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index); + ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index); + + ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time); + ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time); + + ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible); + ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility); + + ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset); + ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset); + + ClassDB::bind_method(D_METHOD("get_color"), &CharFXTransform::get_color); + ClassDB::bind_method(D_METHOD("set_color", "color"), &CharFXTransform::set_color); + + ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment); + ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment); + + ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character); + ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character); + + ClassDB::bind_method(D_METHOD("get_value_or", "key", "default_value"), &CharFXTransform::get_value_or); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "elapsed_time"), "set_elapsed_time", "get_elapsed_time"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character"); +} + +Variant CharFXTransform::get_value_or(String p_key, Variant p_default_value) { + if (!this->environment.has(p_key)) + return p_default_value; + + Variant r = environment[p_key]; + if (r.get_type() != p_default_value.get_type()) + return p_default_value; + + return r; +} + +CharFXTransform::CharFXTransform() { + relative_index = 0; + absolute_index = 0; + visibility = true; + offset = Point2(); + color = Color(); + character = 0; +} diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h new file mode 100644 index 0000000000..f9c3e15399 --- /dev/null +++ b/scene/gui/rich_text_effect.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* rich_text_effect.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef RICH_TEXT_EFFECT_H +#define RICH_TEXT_EFFECT_H + +#include "core/resource.h" + +class RichTextEffect : public Resource { + GDCLASS(RichTextEffect, Resource); + OBJ_SAVE_TYPE(RichTextEffect); + +protected: + static void _bind_methods(); + +public: + Variant get_bbcode() const; + bool _process_effect_impl(Ref<class CharFXTransform> p_cfx); + + RichTextEffect(); +}; + +class CharFXTransform : public Reference { + GDCLASS(CharFXTransform, Reference); + +protected: + static void _bind_methods(); + +public: + uint64_t relative_index; + uint64_t absolute_index; + bool visibility; + Point2 offset; + Color color; + CharType character; + float elapsed_time; + Dictionary environment; + + CharFXTransform(); + uint64_t get_relative_index() { return relative_index; } + void set_relative_index(uint64_t p_index) { relative_index = p_index; } + uint64_t get_absolute_index() { return absolute_index; } + void set_absolute_index(uint64_t p_index) { absolute_index = p_index; } + float get_elapsed_time() { return elapsed_time; } + void set_elapsed_time(float p_elapsed_time) { elapsed_time = p_elapsed_time; } + bool is_visible() { return visibility; } + void set_visibility(bool p_vis) { visibility = p_vis; } + Point2 get_offset() { return offset; } + void set_offset(Point2 p_offset) { offset = p_offset; } + Color get_color() { return color; } + void set_color(Color p_color) { color = p_color; } + int get_character() { return (int)character; } + void set_character(int p_char) { character = (CharType)p_char; } + Dictionary get_environment() { return environment; } + void set_environment(Dictionary p_environment) { environment = p_environment; } + + Variant get_value_or(String p_key, Variant p_default_value); +}; + +#endif // RICH_TEXT_EFFECT_H diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 8223ea6d1e..d9ae42d6e6 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -30,10 +30,11 @@ #include "rich_text_label.h" +#include "core/math/math_defs.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "modules/regex/regex.h" #include "scene/scene_string_names.h" - #ifdef TOOLS_ENABLED #include "editor/editor_scale.h" #endif @@ -139,6 +140,7 @@ Rect2 RichTextLabel::_get_text_rect() { Ref<StyleBox> style = get_stylebox("normal"); return Rect2(style->get_offset(), get_size() - style->get_minimum_size()); } + int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { RID ci; @@ -292,7 +294,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & Color selection_bg; if (p_mode == PROCESS_DRAW) { - selection_fg = get_color("font_color_selected"); selection_bg = get_color("selection_color"); } @@ -343,18 +344,24 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & Color font_color_shadow; bool underline = false; bool strikethrough = false; + ItemFade *fade = NULL; + int it_char_start = p_char_count; + + Vector<ItemFX *> fx_stack = Vector<ItemFX *>(); + bool custom_fx_ok = true; if (p_mode == PROCESS_DRAW) { color = _find_color(text, p_base_color); font_color_shadow = _find_color(text, p_font_color_shadow); if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { - underline = true; } else if (_find_strikethrough(text)) { - strikethrough = true; } + fade = _fetch_by_type<ItemFade>(text, ITEM_FADE); + _fetch_item_stack<ItemFX>(text, fx_stack); + } else if (p_mode == PROCESS_CACHE) { l.char_count += text->text.length(); } @@ -431,8 +438,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & ofs += cw; } else if (p_mode == PROCESS_DRAW) { - bool selected = false; + Color fx_color = Color(color); + Point2 fx_offset; + CharType fx_char = c[i]; + if (selection.active) { int cofs = (&c[i]) - cf; @@ -442,8 +452,78 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } int cw = 0; + int c_item_offset = p_char_count - it_char_start; + + float faded_visibility = 1.0f; + if (fade) { + if (c_item_offset >= fade->starting_index) { + faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; + faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; + } + fx_color.a = faded_visibility; + } + + bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) && + faded_visibility > 0.0f); + + for (int j = 0; j < fx_stack.size(); j++) { + ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]); + ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]); + ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]); + ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]); + ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]); + + if (item_custom && custom_fx_ok) { + Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform)); + Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier); + if (!custom_effect.is_null()) { + charfx->elapsed_time = item_custom->elapsed_time; + charfx->environment = item_custom->environment; + charfx->relative_index = c_item_offset; + charfx->absolute_index = p_char_count; + charfx->visibility = visible; + charfx->offset = fx_offset; + charfx->color = fx_color; + charfx->character = fx_char; + + bool effect_status = custom_effect->_process_effect_impl(charfx); + custom_fx_ok = effect_status; + + fx_offset += charfx->offset; + fx_color = charfx->color; + visible &= charfx->visibility; + fx_char = charfx->character; + } + } else if (item_shake) { + uint64_t char_current_rand = item_shake->offset_random(c_item_offset); + uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + fx_offset += Point2(Math::lerp(Math::sin(previous_offset), + Math::sin(current_offset), + n_time), + Math::lerp(Math::cos(previous_offset), + Math::cos(current_offset), + n_time)) * + (float)item_shake->strength / 10.0f; + } else if (item_wave) { + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); + fx_offset += Point2(0, 1) * value; + } else if (item_tornado) { + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + fx_offset += Point2(torn_x, torn_y); + } else if (item_rainbow) { + fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), + item_rainbow->saturation, + item_rainbow->value, + fx_color.a); + } + } - bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)); if (visible) line_is_blank = false; @@ -451,27 +531,28 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & visible = false; if (visible) { + if (selected) { - cw = font->get_char_size(c[i], c[i + 1]).x; + cw = font->get_char_size(fx_char, c[i + 1]).x; draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); } if (p_font_color_shadow.a > 0) { float x_ofs_shadow = align_ofs + pofs; float y_ofs_shadow = y + lh - line_descent; - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, fx_char, c[i + 1], p_font_color_shadow); if (p_shadow_as_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); } } if (selected) { - drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color); + drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); } else { - cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color); + cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); } } @@ -800,6 +881,31 @@ void RichTextLabel::_update_scroll() { } } +void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) { + Item *it = p_frame; + while (it) { + ItemFX *ifx = Object::cast_to<ItemFX>(it); + + if (!ifx) { + it = _get_next_item(it, true); + continue; + } + + ifx->elapsed_time += p_delta_time; + + ItemShake *shake = Object::cast_to<ItemShake>(it); + if (shake) { + bool cycle = (shake->elapsed_time > (1.0f / shake->rate)); + if (cycle) { + shake->elapsed_time -= (1.0f / shake->rate); + shake->reroll_random(); + } + } + + it = _get_next_item(it, true); + } +} + void RichTextLabel::_notification(int p_what) { switch (p_what) { @@ -873,6 +979,15 @@ void RichTextLabel::_notification(int p_what) { from_line++; } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + float dt = get_process_delta_time(); + + for (int i = 0; i < custom_effects.size(); i++) { + } + + _update_fx(main, dt); + update(); } } } @@ -916,9 +1031,12 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const { - if (!underline_meta || selection.click) + if (!underline_meta) return CURSOR_ARROW; + if (selection.click) + return CURSOR_IBEAM; + if (main->first_invalid_line < main->lines.size()) return CURSOR_ARROW; //invalid @@ -1023,15 +1141,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } if (b->get_button_index() == BUTTON_WHEEL_UP) { - if (scroll_active) - vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8); } if (b->get_button_index() == BUTTON_WHEEL_DOWN) { - if (scroll_active) - vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8); } } @@ -1282,8 +1396,19 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) { return false; } -bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { +bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) { + Item *item = p_item; + while (item) { + if (item->type == p_type) { + return true; + } + item = item->parent; + } + return false; +} + +bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { Item *item = p_item; while (item) { @@ -1615,6 +1740,49 @@ void RichTextLabel::push_table(int p_columns) { _add_item(item, true, true); } +void RichTextLabel::push_fade(int p_start_index, int p_length) { + ItemFade *item = memnew(ItemFade); + item->starting_index = p_start_index; + item->length = p_length; + _add_item(item, true); +} + +void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) { + ItemShake *item = memnew(ItemShake); + item->strength = p_strength; + item->rate = p_rate; + _add_item(item, true); +} + +void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) { + ItemWave *item = memnew(ItemWave); + item->frequency = p_frequency; + item->amplitude = p_amplitude; + _add_item(item, true); +} + +void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) { + ItemTornado *item = memnew(ItemTornado); + item->frequency = p_frequency; + item->radius = p_radius; + _add_item(item, true); +} + +void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) { + ItemRainbow *item = memnew(ItemRainbow); + item->frequency = p_frequency; + item->saturation = p_saturation; + item->value = p_value; + _add_item(item, true); +} + +void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) { + ItemCustomFX *item = memnew(ItemCustomFX); + item->identifier = p_identifier; + item->environment = p_environment; + _add_item(item, true); +} + void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) { ERR_FAIL_COND(current->type != ITEM_TABLE); @@ -1759,6 +1927,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { bool in_bold = false; bool in_italics = false; + set_process_internal(false); + while (pos < p_bbcode.length()) { int brk_pos = p_bbcode.find("[", pos); @@ -1782,7 +1952,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1); - if (tag.begins_with("/") && tag_stack.size()) { bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); @@ -1795,9 +1964,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { indent_level--; if (!tag_ok) { - - add_text("["); - pos++; + add_text("[" + tag); + pos = brk_end; continue; } @@ -1989,10 +2157,145 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("font"); - } else { + } else if (tag.begins_with("fade")) { + Vector<String> tags = tag.split(" ", false); + int startIndex = 0; + int length = 10; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("start=")) { + String start_str = expr.substr(6, expr.length()); + startIndex = start_str.to_int(); + } else if (expr.begins_with("length=")) { + String end_str = expr.substr(7, expr.length()); + length = end_str.to_int(); + } + } + } + + push_fade(startIndex, length); + pos = brk_end + 1; + tag_stack.push_front("fade"); + } else if (tag.begins_with("shake")) { + Vector<String> tags = tag.split(" ", false); + int strength = 5; + float rate = 20.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("level=")) { + String str_str = expr.substr(6, expr.length()); + strength = str_str.to_int(); + } else if (expr.begins_with("rate=")) { + String rate_str = expr.substr(5, expr.length()); + rate = rate_str.to_float(); + } + } + } + + push_shake(strength, rate); + pos = brk_end + 1; + tag_stack.push_front("shake"); + set_process_internal(true); + } else if (tag.begins_with("wave")) { + Vector<String> tags = tag.split(" ", false); + float amplitude = 20.0f; + float period = 5.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("amp=")) { + String amp_str = expr.substr(4, expr.length()); + amplitude = amp_str.to_float(); + } else if (expr.begins_with("freq=")) { + String period_str = expr.substr(5, expr.length()); + period = period_str.to_float(); + } + } + } - add_text("["); //ignore - pos = brk_pos + 1; + push_wave(period, amplitude); + pos = brk_end + 1; + tag_stack.push_front("wave"); + set_process_internal(true); + } else if (tag.begins_with("tornado")) { + Vector<String> tags = tag.split(" ", false); + float radius = 10.0f; + float frequency = 1.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("radius=")) { + String amp_str = expr.substr(7, expr.length()); + radius = amp_str.to_float(); + } else if (expr.begins_with("freq=")) { + String period_str = expr.substr(5, expr.length()); + frequency = period_str.to_float(); + } + } + } + + push_tornado(frequency, radius); + pos = brk_end + 1; + tag_stack.push_front("tornado"); + set_process_internal(true); + } else if (tag.begins_with("rainbow")) { + Vector<String> tags = tag.split(" ", false); + float saturation = 0.8f; + float value = 0.8f; + float frequency = 1.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("sat=")) { + String sat_str = expr.substr(4, expr.length()); + saturation = sat_str.to_float(); + } else if (expr.begins_with("val=")) { + String val_str = expr.substr(4, expr.length()); + value = val_str.to_float(); + } else if (expr.begins_with("freq=")) { + String freq_str = expr.substr(5, expr.length()); + frequency = freq_str.to_float(); + } + } + } + + push_rainbow(saturation, value, frequency); + pos = brk_end + 1; + tag_stack.push_front("rainbow"); + set_process_internal(true); + } else { + Vector<String> expr = tag.split(" ", false); + if (expr.size() < 1) { + add_text("["); + pos = brk_pos + 1; + } else { + String identifier = expr[0]; + expr.remove(0); + Dictionary properties = parse_expressions_for_values(expr); + Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier); + + if (!effect.is_null()) { + push_customfx(identifier, properties); + pos = brk_end + 1; + tag_stack.push_front(identifier); + set_process_internal(true); + } else { + add_text("["); //ignore + pos = brk_pos + 1; + } + } } } @@ -2201,6 +2504,34 @@ float RichTextLabel::get_percent_visible() const { return percent_visible; } +void RichTextLabel::set_effects(const Vector<Variant> &effects) { + custom_effects.clear(); + for (int i = 0; i < effects.size(); i++) { + Ref<RichTextEffect> effect = Ref<RichTextEffect>(effects[i]); + custom_effects.push_back(effect); + } + + parse_bbcode(bbcode); +} + +Vector<Variant> RichTextLabel::get_effects() { + Vector<Variant> r; + for (int i = 0; i < custom_effects.size(); i++) { + r.push_back(custom_effects[i].get_ref_ptr()); + } + return r; +} + +void RichTextLabel::install_effect(const Variant effect) { + Ref<RichTextEffect> rteffect; + rteffect = effect; + + if (rteffect.is_valid()) { + custom_effects.push_back(effect); + parse_bbcode(bbcode); + } +} + int RichTextLabel::get_content_height() { int total_height = 0; if (main->lines.size()) @@ -2277,6 +2608,12 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height); + ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values); + + ClassDB::bind_method(D_METHOD("set_effects", "effects"), &RichTextLabel::set_effects); + ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects); + ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect); + ADD_GROUP("BBCode", "bbcode_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode"); @@ -2294,6 +2631,8 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_RESOURCE_TYPE, "17/17:RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "RichTextEffect"), "set_effects", "get_effects"); + 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))); @@ -2319,11 +2658,16 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_INDENT); BIND_ENUM_CONSTANT(ITEM_LIST); BIND_ENUM_CONSTANT(ITEM_TABLE); + BIND_ENUM_CONSTANT(ITEM_FADE); + BIND_ENUM_CONSTANT(ITEM_SHAKE); + BIND_ENUM_CONSTANT(ITEM_WAVE); + BIND_ENUM_CONSTANT(ITEM_TORNADO); + BIND_ENUM_CONSTANT(ITEM_RAINBOW); + BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); BIND_ENUM_CONSTANT(ITEM_META); } void RichTextLabel::set_visible_characters(int p_visible) { - visible_characters = p_visible; update(); } @@ -2355,6 +2699,77 @@ Size2 RichTextLabel::get_minimum_size() const { return Size2(); } +Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) { + Ref<RichTextEffect> r; + for (int i = 0; i < custom_effects.size(); i++) { + if (!custom_effects[i].is_valid()) + continue; + + if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) { + r = custom_effects[i]; + } + } + + return r; +} + +Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) { + Dictionary d = Dictionary(); + for (int i = 0; i < p_expressions.size(); i++) { + String expression = p_expressions[i]; + + Array a = Array(); + Vector<String> parts = expression.split("=", true); + String key = parts[0]; + if (parts.size() != 2) { + return d; + } + + Vector<String> values = parts[1].split(",", false); + + RegEx color = RegEx(); + color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"); + RegEx nodepath = RegEx(); + nodepath.compile("^\\$"); + RegEx boolean = RegEx(); + boolean.compile("^(true|false)$"); + RegEx decimal = RegEx(); + decimal.compile("^-?^.?\\d+(\\.\\d+?)?$"); + RegEx numerical = RegEx(); + numerical.compile("^\\d+$"); + + for (int j = 0; j < values.size(); j++) { + if (!color.search(values[j]).is_null()) { + a.append(Color::html(values[j])); + } else if (!nodepath.search(values[j]).is_null()) { + if (values[j].begins_with("$")) { + String v = values[j].substr(1, values[j].length()); + a.append(NodePath(v)); + } + } else if (!boolean.search(values[j]).is_null()) { + if (values[j] == "true") { + a.append(true); + } else if (values[j] == "false") { + a.append(false); + } + } else if (!decimal.search(values[j]).is_null()) { + a.append(values[j].to_double()); + } else if (!numerical.search(values[j]).is_null()) { + a.append(values[j].to_int()); + } else { + a.append(values[j]); + } + } + + if (values.size() > 1) { + d[key] = a; + } else if (values.size() == 1) { + d[key] = a[0]; + } + } + return d; +} + RichTextLabel::RichTextLabel() { main = memnew(ItemFrame); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 21d099c37a..1c212700ef 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -31,6 +31,7 @@ #ifndef RICH_TEXT_LABEL_H #define RICH_TEXT_LABEL_H +#include "rich_text_effect.h" #include "scene/gui/scroll_bar.h" class RichTextLabel : public Control { @@ -67,7 +68,13 @@ public: ITEM_INDENT, ITEM_LIST, ITEM_TABLE, - ITEM_META + ITEM_FADE, + ITEM_SHAKE, + ITEM_WAVE, + ITEM_TORNADO, + ITEM_RAINBOW, + ITEM_META, + ITEM_CUSTOMFX }; protected: @@ -96,7 +103,7 @@ private: } }; - struct Item { + struct Item : public Object { int index; Item *parent; @@ -214,6 +221,101 @@ private: ItemTable() { type = ITEM_TABLE; } }; + struct ItemFade : public Item { + int starting_index; + int length; + + ItemFade() { type = ITEM_FADE; } + }; + + struct ItemFX : public Item { + float elapsed_time; + + ItemFX() { + elapsed_time = 0.0f; + } + }; + + struct ItemShake : public ItemFX { + int strength; + float rate; + uint64_t _current_rng; + uint64_t _previous_rng; + + ItemShake() { + strength = 0; + rate = 0.0f; + _current_rng = 0; + type = ITEM_SHAKE; + } + + void reroll_random() { + _previous_rng = _current_rng; + _current_rng = Math::rand(); + } + + uint64_t offset_random(int index) { + return (_current_rng >> (index % 64)) | + (_current_rng << (64 - (index % 64))); + } + + uint64_t offset_previous_random(int index) { + return (_previous_rng >> (index % 64)) | + (_previous_rng << (64 - (index % 64))); + } + }; + + struct ItemWave : public ItemFX { + float frequency; + float amplitude; + + ItemWave() { + frequency = 1.0f; + amplitude = 1.0f; + type = ITEM_WAVE; + } + }; + + struct ItemTornado : public ItemFX { + float radius; + float frequency; + + ItemTornado() { + radius = 1.0f; + frequency = 1.0f; + type = ITEM_TORNADO; + } + }; + + struct ItemRainbow : public ItemFX { + float saturation; + float value; + float frequency; + + ItemRainbow() { + saturation = 0.8f; + value = 0.8f; + frequency = 1.0f; + type = ITEM_RAINBOW; + } + }; + + struct ItemCustomFX : public ItemFX { + String identifier; + Dictionary environment; + + ItemCustomFX() { + identifier = ""; + environment = Dictionary(); + type = ITEM_CUSTOMFX; + } + + virtual ~ItemCustomFX() { + _clear_children(); + environment.clear(); + } + }; + ItemFrame *main; Item *current; ItemFrame *current_frame; @@ -239,6 +341,8 @@ private: ItemMeta *meta_hovering; Variant current_meta; + Vector<Ref<RichTextEffect> > custom_effects; + void _invalidate_current_line(ItemFrame *p_frame); void _validate_line_caches(ItemFrame *p_frame); @@ -246,7 +350,6 @@ private: void _remove_item(Item *p_item, const int p_line, const int p_subitem_line); struct ProcessState { - int line_width; }; @@ -287,8 +390,36 @@ private: bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL); bool _find_layout_subitem(Item *from, Item *to); + bool _find_by_type(Item *p_item, ItemType p_type); + template <typename T> + T *_fetch_by_type(Item *p_item, ItemType p_type) { + Item *item = p_item; + T *result = NULL; + while (item) { + if (item->type == p_type) { + result = Object::cast_to<T>(item); + if (result) + return result; + } + item = item->parent; + } + + return result; + }; + template <typename T> + void _fetch_item_stack(Item *p_item, Vector<T *> &r_stack) { + Item *item = p_item; + while (item) { + T *found = Object::cast_to<T>(item); + if (found) { + r_stack.push_back(found); + } + item = item->parent; + } + } void _update_scroll(); + void _update_fx(ItemFrame *p_frame, float p_delta_time); void _scroll_changed(double); void _gui_input(Ref<InputEvent> p_event); @@ -296,6 +427,8 @@ private: Item *_get_prev_item(Item *p_item, bool p_free = false); Rect2 _get_text_rect(); + Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier); + virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions); bool use_bbcode; String bbcode; @@ -322,6 +455,12 @@ public: void push_list(ListType p_list); void push_meta(const Variant &p_meta); void push_table(int p_columns); + void push_fade(int p_start_index, int p_length); + void push_shake(int p_strength, float p_rate); + void push_wave(float p_frequency, float p_amplitude); + void push_tornado(float p_frequency, float p_radius); + void push_rainbow(float p_saturation, float p_value, float p_frequency); + void push_customfx(String p_identifier, Dictionary p_environment); void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1); int get_current_table_column() const; void push_cell(); @@ -380,6 +519,11 @@ public: void set_percent_visible(float p_percent); float get_percent_visible() const; + void set_effects(const Vector<Variant> &effects); + Vector<Variant> get_effects(); + + void install_effect(const Variant effect); + void set_fixed_size_to_width(int p_width); virtual Size2 get_minimum_size() const; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 461281a4ed..a840e3fec1 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -382,7 +382,10 @@ void ScrollContainer::update_scrollbars() { Size2 min = child_max_size; - if (!scroll_v || min.height <= size.height - hmin.height) { + bool hide_scroll_v = !scroll_v || min.height <= size.height - hmin.height; + bool hide_scroll_h = !scroll_h || min.width <= size.width - vmin.width; + + if (hide_scroll_v) { v_scroll->hide(); v_scroll->set_max(0); @@ -391,11 +394,16 @@ void ScrollContainer::update_scrollbars() { v_scroll->show(); v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); + if (hide_scroll_h) { + v_scroll->set_page(size.height); + } else { + v_scroll->set_page(size.height - hmin.height); + } + scroll.y = v_scroll->get_value(); } - if (!scroll_h || min.width <= size.width - vmin.width) { + if (hide_scroll_h) { h_scroll->hide(); h_scroll->set_max(0); @@ -404,7 +412,12 @@ void ScrollContainer::update_scrollbars() { h_scroll->show(); h_scroll->set_max(min.width); - h_scroll->set_page(size.width - vmin.width); + if (hide_scroll_v) { + h_scroll->set_page(size.width); + } else { + h_scroll->set_page(size.width - vmin.width); + } + scroll.x = h_scroll->get_value(); } } diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 6ada0cba97..172c366c41 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "spin_box.h" +#include "core/math/expression.h" #include "core/os/input.h" Size2 SpinBox::get_minimum_size() const { @@ -50,15 +51,19 @@ void SpinBox::_value_changed(double) { void SpinBox::_text_entered(const String &p_string) { - /* - if (!p_string.is_numeric()) + Ref<Expression> expr; + expr.instance(); + // Ignore the prefix and suffix in the expression + Error err = expr->parse(p_string.trim_prefix(prefix + " ").trim_suffix(" " + suffix)); + if (err != OK) { return; - */ - String value = p_string; - if (prefix != "" && p_string.begins_with(prefix)) - value = p_string.substr(prefix.length(), p_string.length() - prefix.length()); - set_value(value.to_double()); - _value_changed(0); + } + + Variant value = expr->execute(Array(), NULL, false); + if (value.get_type() != Variant::NIL) { + set_value(value); + _value_changed(0); + } } LineEdit *SpinBox::get_line_edit() { diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index be8f1cf36e..292d80be9d 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -840,7 +840,7 @@ Size2 TabContainer::get_minimum_size() const { Control *c = tabs[i]; - if (!c->is_visible_in_tree()) + if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size) continue; Size2 cms = c->get_combined_minimum_size(); @@ -887,6 +887,13 @@ int TabContainer::get_tabs_rearrange_group() const { return tabs_rearrange_group; } +void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) { + use_hidden_tabs_for_min_size = p_use_hidden_tabs; +} + +bool TabContainer::get_use_hidden_tabs_for_min_size() const { + return use_hidden_tabs_for_min_size; +} void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input); @@ -913,6 +920,9 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group); ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group); + ClassDB::bind_method(D_METHOD("set_use_hidden_tabs_for_min_size", "enabled"), &TabContainer::set_use_hidden_tabs_for_min_size); + ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size); + ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback); ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed); ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab); @@ -925,6 +935,7 @@ void TabContainer::_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_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size"); BIND_ENUM_CONSTANT(ALIGN_LEFT); BIND_ENUM_CONSTANT(ALIGN_CENTER); @@ -945,4 +956,5 @@ TabContainer::TabContainer() { popup = NULL; drag_to_rearrange_enabled = false; tabs_rearrange_group = -1; + use_hidden_tabs_for_min_size = false; } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index f7a9fb64fd..0314f86837 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -59,6 +59,7 @@ private: int _get_top_margin() const; Popup *popup; bool drag_to_rearrange_enabled; + bool use_hidden_tabs_for_min_size; int tabs_rearrange_group; Vector<Control *> _get_tabs() const; @@ -118,6 +119,8 @@ public: bool get_drag_to_rearrange_enabled() const; void set_tabs_rearrange_group(int p_group_id); int get_tabs_rearrange_group() const; + void set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs); + bool get_use_hidden_tabs_for_min_size() const; TabContainer(); }; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1d434e5a2a..d5f1d317c7 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -596,8 +596,14 @@ void TextEdit::_update_minimap_drag() { return; } + int control_height = _get_control_height(); + int scroll_height = v_scroll->get_max() * (minimap_char_size.y + minimap_line_spacing); + if (control_height > scroll_height) { + control_height = scroll_height; + } + Point2 mp = get_local_mouse_position(); - double diff = (mp.y - minimap_scroll_click_pos) / _get_control_height(); + double diff = (mp.y - minimap_scroll_click_pos) / control_height; v_scroll->set_as_ratio(minimap_scroll_ratio + diff); } @@ -605,7 +611,6 @@ void TextEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - _update_caches(); if (cursor_changed_dirty) MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit"); @@ -614,12 +619,16 @@ void TextEdit::_notification(int p_what) { _update_wrap_at(); } break; case NOTIFICATION_RESIZED: { - _update_scrollbars(); _update_wrap_at(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (is_visible()) { + call_deferred("_update_scrollbars"); + call_deferred("_update_wrap_at"); + } + } break; case NOTIFICATION_THEME_CHANGED: { - _update_caches(); _update_wrap_at(); syntax_highlighting_cache.clear(); @@ -638,7 +647,7 @@ void TextEdit::_notification(int p_what) { if (scrolling && get_v_scroll() != target_v_scroll) { double target_y = target_v_scroll - get_v_scroll(); double dist = sqrt(target_y * target_y); - // To ensure minimap is responsive overide the speed setting. + // To ensure minimap is responsive override the speed setting. double vel = ((target_y / dist) * ((minimap_clicked) ? 3000 : v_scroll_speed)) * get_physics_process_delta_time(); if (Math::abs(vel) >= dist) { @@ -1729,7 +1738,9 @@ void TextEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - if (!caret_blink_enabled) { + if (caret_blink_enabled) { + caret_blink_timer->start(); + } else { draw_caret = true; } @@ -1742,6 +1753,10 @@ void TextEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_EXIT: { + if (caret_blink_enabled) { + caret_blink_timer->stop(); + } + OS::get_singleton()->set_ime_position(Point2()); OS::get_singleton()->set_ime_active(false); ime_text = ""; @@ -2066,6 +2081,44 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co r_col = col; } +Vector2i TextEdit::_get_cursor_pixel_pos() { + adjust_viewport_to_cursor(); + int row = (cursor.line - get_first_visible_line() - cursor.wrap_ofs); + // Correct for hidden and wrapped lines + for (int i = get_first_visible_line(); i < cursor.line; i++) { + if (is_line_hidden(i)) { + row -= 1; + continue; + } + row += times_line_wraps(i); + } + // Row might be wrapped. Adjust row and r_column + Vector<String> rows2 = get_wrap_rows_text(cursor.line); + while (rows2.size() > 1) { + if (cursor.column >= rows2[0].length()) { + cursor.column -= rows2[0].length(); + rows2.remove(0); + row++; + } else { + break; + } + } + + // Calculate final pixel position + int y = (row - get_v_scroll_offset() + 1 /*Bottom of line*/) * get_row_height(); + int x = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs; + int ix = 0; + while (ix < rows2[0].size() && ix < cursor.column) { + if (cache.font != NULL) { + x += cache.font->get_char_size(rows2[0].get(ix)).width; + } + ix++; + } + x += get_indent_level(cursor.line) * cache.font->get_char_size(' ').width; + + return Vector2i(x, y); +} + void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const { float rows = p_mouse.y; @@ -2194,12 +2247,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { int row, col; _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); - if (mb->get_command() && highlighted_word != String()) { - - emit_signal("symbol_lookup", highlighted_word, row, col); - return; - } - // Toggle breakpoint on gutter click. if (draw_breakpoint_gutter) { int gutter = cache.style_normal->get_margin(MARGIN_LEFT); @@ -2368,6 +2415,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } else { if (mb->get_button_index() == BUTTON_LEFT) { + if (mb->get_command() && highlighted_word != String()) { + int row, col; + _get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col); + + emit_signal("symbol_lookup", highlighted_word, row, col); + return; + } + dragging_minimap = false; dragging_selection = false; can_drag_minimap = false; @@ -2400,7 +2455,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mm.is_valid()) { if (select_identifiers_enabled) { - if (mm->get_command() && mm->get_button_mask() == 0) { + if (!dragging_minimap && !dragging_selection && mm->get_command() && mm->get_button_mask() == 0) { String new_word = get_word_at_pos(mm->get_position()); if (new_word != highlighted_word) { @@ -2458,7 +2513,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { #endif if (select_identifiers_enabled) { - if (k->is_pressed()) { + if (k->is_pressed() && !dragging_minimap && !dragging_selection) { highlighted_word = get_word_at_pos(get_local_mouse_position()); update(); @@ -3205,7 +3260,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (readonly) break; - if (k->get_shift() && !k->get_command() && !k->get_alt()) { + if (k->get_shift() && !k->get_command() && !k->get_alt() && is_shortcut_keys_enabled()) { cut(); break; } @@ -3436,13 +3491,15 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - select_all(); + if (is_shortcut_keys_enabled()) { + select_all(); + } #else if ((!k->get_command() && !k->get_control())) { scancode_handled = false; break; } - if (!k->get_shift() && k->get_command()) + if (!k->get_shift() && k->get_command() && is_shortcut_keys_enabled()) select_all(); else if (k->get_control()) { if (k->get_shift()) @@ -3498,8 +3555,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { scancode_handled = false; break; } - - cut(); + if (is_shortcut_keys_enabled()) { + cut(); + } } break; case KEY_C: { @@ -3509,7 +3567,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } - copy(); + if (is_shortcut_keys_enabled()) { + copy(); + } } break; case KEY_Z: { @@ -3523,10 +3583,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } - if (k->get_shift()) - redo(); - else - undo(); + if (is_shortcut_keys_enabled()) { + if (k->get_shift()) + redo(); + else + undo(); + } } break; case KEY_Y: { @@ -3539,7 +3601,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } - redo(); + if (is_shortcut_keys_enabled()) { + redo(); + } } break; case KEY_V: { if (readonly) { @@ -3550,7 +3614,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; } - paste(); + if (is_shortcut_keys_enabled()) { + paste(); + } } break; case KEY_SPACE: { @@ -3568,6 +3634,16 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } break; + case KEY_MENU: { + if (context_menu_enabled) { + menu->set_position(get_global_transform().xform(_get_cursor_pixel_pos())); + menu->set_size(Vector2(1, 1)); + menu->set_scale(get_global_transform().get_scale()); + menu->popup(); + menu->grab_focus(); + } + } break; + default: { scancode_handled = false; @@ -4044,6 +4120,25 @@ int TextEdit::_get_control_height() const { return control_height; } +void TextEdit::_generate_context_menu() { + // Reorganize context menu. + menu->clear(); + if (!readonly) + menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_X : 0); + menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_C : 0); + if (!readonly) + menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_V : 0); + menu->add_separator(); + if (is_selecting_enabled()) + menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_A : 0); + if (!readonly) { + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_Z : 0); + menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z : 0); + } +} + int TextEdit::get_visible_rows() const { return _get_control_height() / get_row_height(); } @@ -4385,11 +4480,14 @@ bool TextEdit::cursor_get_blink_enabled() const { void TextEdit::cursor_set_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; - if (p_enabled) { - caret_blink_timer->start(); - } else { - caret_blink_timer->stop(); + if (has_focus()) { + if (p_enabled) { + caret_blink_timer->start(); + } else { + caret_blink_timer->stop(); + } } + draw_caret = true; } @@ -4613,7 +4711,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const { return CURSOR_ARROW; } else { int xmargin_end = get_size().width - cache.style_normal->get_margin(MARGIN_RIGHT); - if (p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) { + if (draw_minimap && p_pos.x > xmargin_end - minimap_width && p_pos.x <= xmargin_end) { return CURSOR_ARROW; } @@ -4749,6 +4847,7 @@ void TextEdit::set_readonly(bool p_readonly) { return; readonly = p_readonly; + _generate_context_menu(); // Reorganize context menu. menu->clear(); @@ -4806,10 +4905,12 @@ int TextEdit::get_max_chars() const { void TextEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { - caret_blink_timer->stop(); - caret_blink_timer->start(); draw_caret = true; - update(); + if (has_focus()) { + caret_blink_timer->stop(); + caret_blink_timer->start(); + update(); + } } } @@ -5086,6 +5187,8 @@ void TextEdit::paste() { } void TextEdit::select_all() { + if (!selecting_enabled) + return; if (text.size() == 1 && text[0].length() == 0) return; @@ -5110,6 +5213,8 @@ void TextEdit::deselect() { } void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { + if (!selecting_enabled) + return; if (p_from_line < 0) p_from_line = 0; @@ -5354,6 +5459,9 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l break; } pos_from = last_pos - p_key.length(); + if (pos_from < 0) { + break; + } } } else { while ((last_pos = (p_search_flags & SEARCH_MATCH_CASE) ? text_line.find(p_key, pos_from) : text_line.findn(p_key, pos_from)) != -1) { @@ -6222,7 +6330,7 @@ void TextEdit::_confirm_completion() { CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1]; if ((last_completion_char == '"' || last_completion_char == '\'') && last_completion_char == next_char) { - _base_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); + _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1); } if (last_completion_char == '(') { @@ -6750,6 +6858,29 @@ bool TextEdit::is_context_menu_enabled() { return context_menu_enabled; } +void TextEdit::set_shortcut_keys_enabled(bool p_enabled) { + shortcut_keys_enabled = p_enabled; + + _generate_context_menu(); +} + +void TextEdit::set_selecting_enabled(bool p_enabled) { + selecting_enabled = p_enabled; + + if (!selecting_enabled) + deselect(); + + _generate_context_menu(); +} + +bool TextEdit::is_selecting_enabled() const { + return selecting_enabled; +} + +bool TextEdit::is_shortcut_keys_enabled() const { + return shortcut_keys_enabled; +} + PopupMenu *TextEdit::get_menu() const { return menu; } @@ -6805,6 +6936,10 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled); ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled); + ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled); + ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &TextEdit::set_selecting_enabled); + ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &TextEdit::is_selecting_enabled); ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut); ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy); @@ -6895,6 +7030,8 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_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"); @@ -7053,7 +7190,9 @@ TextEdit::TextEdit() { minimap_char_size = Point2(1, 2); minimap_line_spacing = 1; + selecting_enabled = true; context_menu_enabled = true; + shortcut_keys_enabled = true; menu = memnew(PopupMenu); add_child(menu); readonly = true; // Initialise to opposite first, so we get past the early-out in set_readonly. diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 9c568acd93..e5d9b006fe 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -365,10 +365,15 @@ private: int search_result_line; int search_result_col; + bool selecting_enabled; + bool context_menu_enabled; + bool shortcut_keys_enabled; int executing_line; + void _generate_context_menu(); + int get_visible_rows() const; int get_total_visible_rows() const; @@ -582,6 +587,7 @@ public: int cursor_get_column() const; int cursor_get_line() const; + Vector2i _get_cursor_pixel_pos(); bool cursor_get_blink_enabled() const; void cursor_set_blink_enabled(const bool p_enabled); @@ -737,6 +743,12 @@ public: void set_context_menu_enabled(bool p_enable); bool is_context_menu_enabled(); + void set_selecting_enabled(bool p_enabled); + bool is_selecting_enabled() const; + + void set_shortcut_keys_enabled(bool p_enabled); + bool is_shortcut_keys_enabled() const; + PopupMenu *get_menu() const; String get_text_for_completion(); diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index b5f949aeb7..e9b0bd8f38 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -205,24 +205,26 @@ void TextureButton::_notification(int p_what) { case STRETCH_KEEP_ASPECT_COVERED: { size = get_size(); Size2 tex_size = texdraw->get_size(); - Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height); - float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height; - Size2 scaledTexSize = tex_size * scale; - Point2 ofs2 = ((scaledTexSize - size) / scale).abs() / 2.0f; + Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height); + float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height; + Size2 scaled_tex_size = tex_size * scale; + Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f; _texture_region = Rect2(ofs2, size / scale); } break; } } + _position_rect = Rect2(ofs, size); - if (_tile) + if (_tile) { draw_texture_rect(texdraw, _position_rect, _tile); - else + } else { draw_texture_rect_region(texdraw, _position_rect, _texture_region); + } } else { _position_rect = Rect2(); } - if (has_focus() && focused.is_valid()) { + if (has_focus() && focused.is_valid()) { draw_texture_rect(focused, _position_rect, false); }; } break; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index b7451faad3..57663bbe82 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -328,7 +328,7 @@ void TreeItem::set_collapsed(bool p_collapsed) { ci = ci->parent; } - if (ci) { // collapsing cursor/selectd, move it! + if (ci) { // collapsing cursor/selected, move it! if (tree->select_mode == Tree::SELECT_MULTI) { @@ -914,7 +914,6 @@ void Tree::update_cache() { cache.arrow_collapsed = get_icon("arrow_collapsed"); cache.arrow = get_icon("arrow"); cache.select_arrow = get_icon("select_arrow"); - cache.select_option = get_icon("select_option"); cache.updown = get_icon("updown"); cache.custom_button = get_stylebox("custom_button"); @@ -930,7 +929,6 @@ void Tree::update_cache() { cache.vseparation = get_constant("vseparation"); cache.item_margin = get_constant("item_margin"); cache.button_margin = get_constant("button_margin"); - cache.guide_width = get_constant("guide_width"); cache.draw_guides = get_constant("draw_guides"); cache.draw_relationship_lines = get_constant("draw_relationship_lines"); cache.relationship_line_color = get_color("relationship_line_color"); @@ -1184,23 +1182,22 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (p_item->cells[i].selected && select_mode != SELECT_ROW) { - + if (select_mode != SELECT_ROW && (p_item->cells[i].selected || selected_item == p_item)) { Rect2i r(cell_rect.position, cell_rect.size); + if (p_item->cells[i].text.size() > 0) { float icon_width = p_item->cells[i].get_icon_size().width; r.position.x += icon_width; r.size.x -= icon_width; } p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); - if (has_focus()) { - cache.selected_focus->draw(ci, r); - } else { - cache.selected->draw(ci, r); - } - if (text_editor->is_visible_in_tree()) { - Vector2 ofs2(0, (text_editor->get_size().height - r.size.height) / 2); - text_editor->set_position(get_global_position() + r.position - ofs2); + + if (p_item->cells[i].selected) { + if (has_focus()) { + cache.selected_focus->draw(ci, r); + } else { + cache.selected->draw(ci, r); + } } } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index fdc6da5055..f12d8fc4d2 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -416,7 +416,6 @@ private: Ref<Texture> arrow_collapsed; Ref<Texture> arrow; Ref<Texture> select_arrow; - Ref<Texture> select_option; Ref<Texture> updown; Color font_color; @@ -429,7 +428,6 @@ private: int hseparation; int vseparation; int item_margin; - int guide_width; int button_margin; Point2 offset; int draw_relationship_lines; diff --git a/scene/gui/viewport_container.cpp b/scene/gui/viewport_container.cpp index 3f7a110c1b..35696a0459 100644 --- a/scene/gui/viewport_container.cpp +++ b/scene/gui/viewport_container.cpp @@ -211,4 +211,5 @@ ViewportContainer::ViewportContainer() { stretch = false; shrink = 1; set_process_input(true); + set_process_unhandled_input(true); } diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index e21e47f8a8..6c922adbd2 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -60,10 +60,10 @@ Error HTTPRequest::_parse_url(const String &p_url) { use_ssl = true; port = 443; } else { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Malformed URL."); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Malformed URL: " + url + "."); } - ERR_FAIL_COND_V_MSG(url.length() < 1, ERR_INVALID_PARAMETER, "URL too short."); + ERR_FAIL_COND_V_MSG(url.length() < 1, ERR_INVALID_PARAMETER, "URL too short: " + url + "."); int slash_pos = url.find("/"); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0b3a193d18..ba04cb69f2 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1875,6 +1875,19 @@ String Node::get_filename() const { return data.filename; } +void Node::set_editor_description(const String &p_editor_description) { + + set_meta("_editor_description_", p_editor_description); +} +String Node::get_editor_description() const { + + if (has_meta("_editor_description_")) { + return get_meta("_editor_description_"); + } else { + return ""; + } +} + void Node::set_editable_instance(Node *p_node, bool p_editable) { ERR_FAIL_NULL(p_node); @@ -2788,6 +2801,10 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config); ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config); + ClassDB::bind_method(D_METHOD("_set_editor_description", "editor_description"), &Node::set_editor_description); + ClassDB::bind_method(D_METHOD("_get_editor_description"), &Node::get_editor_description); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_editor_description", "_get_editor_description"); + ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); @@ -2844,6 +2861,8 @@ void Node::_bind_methods() { BIND_CONSTANT(NOTIFICATION_WM_ABOUT); BIND_CONSTANT(NOTIFICATION_CRASH); BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE); + BIND_CONSTANT(NOTIFICATION_APP_RESUMED); + BIND_CONSTANT(NOTIFICATION_APP_PAUSED); BIND_ENUM_CONSTANT(PAUSE_MODE_INHERIT); BIND_ENUM_CONSTANT(PAUSE_MODE_STOP); @@ -2860,10 +2879,6 @@ void Node::_bind_methods() { ADD_SIGNAL(MethodInfo("tree_exiting")); ADD_SIGNAL(MethodInfo("tree_exited")); - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/process" ),"set_process","is_processing") ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/physics_process" ), "set_physics_process","is_physics_processing") ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/input" ), "set_process_input","is_processing_input" ) ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); @@ -2887,9 +2902,6 @@ void Node::_bind_methods() { BIND_VMETHOD(MethodInfo("_unhandled_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_key_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEventKey"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_configuration_warning")); - - //ClassDB::bind_method(D_METHOD("get_child",&Node::get_child,PH("index"))); - //ClassDB::bind_method(D_METHOD("get_node",&Node::get_node,PH("path"))); } String Node::_get_name_num_separator() { diff --git a/scene/main/node.h b/scene/main/node.h index 67b40f6dfc..a8bcd2f273 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -250,7 +250,9 @@ public: NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED, NOTIFICATION_WM_ABOUT = MainLoop::NOTIFICATION_WM_ABOUT, NOTIFICATION_CRASH = MainLoop::NOTIFICATION_CRASH, - NOTIFICATION_OS_IME_UPDATE = MainLoop::NOTIFICATION_OS_IME_UPDATE + NOTIFICATION_OS_IME_UPDATE = MainLoop::NOTIFICATION_OS_IME_UPDATE, + NOTIFICATION_APP_RESUMED = MainLoop::NOTIFICATION_APP_RESUMED, + NOTIFICATION_APP_PAUSED = MainLoop::NOTIFICATION_APP_PAUSED }; @@ -318,6 +320,9 @@ public: void set_filename(const String &p_filename); String get_filename() const; + void set_editor_description(const String &p_editor_description); + String get_editor_description() const; + void set_editable_instance(Node *p_node, bool p_editable); bool is_editable_instance(const Node *p_node) const; void set_editable_instances(const HashMap<NodePath, int> &p_editable_instances); @@ -363,8 +368,6 @@ public: Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const; #endif - //Node *clone_tree() const; - // used by editors, to save what has changed only void set_scene_instance_state(const Ref<SceneState> &p_state); Ref<SceneState> get_scene_instance_state() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 617a703855..0465c9305b 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -33,6 +33,7 @@ #include "core/io/marshalls.h" #include "core/io/resource_loader.h" #include "core/message_queue.h" +#include "core/os/dir_access.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/print_string.h" @@ -629,6 +630,7 @@ void SceneTree::_notification(int p_notification) { break; } } break; + case NOTIFICATION_WM_GO_BACK_REQUEST: { get_root()->propagate_notification(p_notification); @@ -638,28 +640,23 @@ void SceneTree::_notification(int p_notification) { break; } } break; - case NOTIFICATION_OS_MEMORY_WARNING: - case NOTIFICATION_OS_IME_UPDATE: - case NOTIFICATION_WM_MOUSE_ENTER: - case NOTIFICATION_WM_MOUSE_EXIT: - case NOTIFICATION_WM_FOCUS_IN: - case NOTIFICATION_WM_FOCUS_OUT: - case NOTIFICATION_WM_ABOUT: { - if (p_notification == NOTIFICATION_WM_FOCUS_IN) { - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); - if (id) { - id->ensure_touch_mouse_raised(); - } + case NOTIFICATION_WM_FOCUS_IN: { + + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + if (id) { + id->ensure_touch_mouse_raised(); } get_root()->propagate_notification(p_notification); } break; + case NOTIFICATION_TRANSLATION_CHANGED: { if (!Engine::get_singleton()->is_editor_hint()) { get_root()->propagate_notification(p_notification); } } break; + case NOTIFICATION_WM_UNFOCUS_REQUEST: { notify_group_flags(GROUP_CALL_REALTIME | GROUP_CALL_MULTILEVEL, "input", NOTIFICATION_WM_UNFOCUS_REQUEST); @@ -668,7 +665,15 @@ void SceneTree::_notification(int p_notification) { } break; - case NOTIFICATION_CRASH: { + case NOTIFICATION_OS_MEMORY_WARNING: + case NOTIFICATION_OS_IME_UPDATE: + case NOTIFICATION_WM_MOUSE_ENTER: + case NOTIFICATION_WM_MOUSE_EXIT: + case NOTIFICATION_WM_FOCUS_OUT: + case NOTIFICATION_WM_ABOUT: + case NOTIFICATION_CRASH: + case NOTIFICATION_APP_RESUMED: + case NOTIFICATION_APP_PAUSED: { get_root()->propagate_notification(p_notification); } break; @@ -1953,6 +1958,38 @@ bool SceneTree::is_using_font_oversampling() const { return use_font_oversampling; } +void SceneTree::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + + if (p_function == "change_scene") { + DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); + List<String> directories; + directories.push_back(dir_access->get_current_dir()); + + while (!directories.empty()) { + dir_access->change_dir(directories.back()->get()); + directories.pop_back(); + + dir_access->list_dir_begin(); + String filename = dir_access->get_next(); + + while (filename != "") { + if (filename == "." || filename == "..") { + filename = dir_access->get_next(); + continue; + } + + if (dir_access->dir_exists(filename)) { + directories.push_back(dir_access->get_current_dir().plus_file(filename)); + } else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) { + r_options->push_back("\"" + dir_access->get_current_dir().plus_file(filename) + "\""); + } + + filename = dir_access->get_next(); + } + } + } +} + SceneTree::SceneTree() { if (singleton == NULL) singleton = this; @@ -2063,6 +2100,7 @@ SceneTree::SceneTree() { if (ScriptDebugger::get_singleton()) { ScriptDebugger::get_singleton()->set_request_scene_tree_message_func(_debugger_request_tree, this); + ScriptDebugger::get_singleton()->set_multiplayer(multiplayer); } root->set_physics_object_picking(GLOBAL_DEF("physics/common/enable_object_picking", true)); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 42a87545a6..d387886d61 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -408,6 +408,7 @@ public: void drop_files(const Vector<String> &p_files, int p_from_screen = 0); void global_menu_action(const Variant &p_id, const Variant &p_meta); + void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; //network API diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 03d46fd28d..14cc705edb 100755 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -107,6 +107,9 @@ bool Timer::has_autostart() const { } void Timer::start(float p_time) { + + ERR_FAIL_COND_MSG(!is_inside_tree(), "Timer was not added to the SceneTree!"); + if (p_time > 0) { set_wait_time(p_time); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 93eea2ad0b..b5c82ce4e3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2289,32 +2289,34 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (from && p_event->is_pressed()) { Control *next = NULL; - if (p_event->is_action_pressed("ui_focus_next")) { + Input *input = Input::get_singleton(); + + if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { next = from->find_next_valid_focus(); } - if (p_event->is_action_pressed("ui_focus_prev")) { + if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) { next = from->find_prev_valid_focus(); } - if (!mods && p_event->is_action_pressed("ui_up")) { + if (!mods && p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) { next = from->_get_focus_neighbour(MARGIN_TOP); } - if (!mods && p_event->is_action_pressed("ui_left")) { + if (!mods && p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) { next = from->_get_focus_neighbour(MARGIN_LEFT); } - if (!mods && p_event->is_action_pressed("ui_right")) { + if (!mods && p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) { next = from->_get_focus_neighbour(MARGIN_RIGHT); } - if (!mods && p_event->is_action_pressed("ui_down")) { + if (!mods && p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) { next = from->_get_focus_neighbour(MARGIN_BOTTOM); } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 06d84302a3..418ee6af0e 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -101,6 +101,7 @@ #include "scene/gui/popup_menu.h" #include "scene/gui/progress_bar.h" #include "scene/gui/reference_rect.h" +#include "scene/gui/rich_text_effect.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/scroll_bar.h" #include "scene/gui/scroll_container.h" @@ -343,6 +344,8 @@ void register_scene_types() { ClassDB::register_class<ColorPicker>(); ClassDB::register_class<ColorPickerButton>(); ClassDB::register_class<RichTextLabel>(); + ClassDB::register_class<RichTextEffect>(); + ClassDB::register_class<CharFXTransform>(); ClassDB::register_class<PopupDialog>(); ClassDB::register_class<WindowDialog>(); ClassDB::register_class<AcceptDialog>(); @@ -361,6 +364,9 @@ void register_scene_types() { /* REGISTER 3D */ + ClassDB::register_class<Skin>(); + ClassDB::register_virtual_class<SkinReference>(); + ClassDB::register_class<Spatial>(); ClassDB::register_virtual_class<SpatialGizmo>(); ClassDB::register_class<Skeleton>(); @@ -528,6 +534,7 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); ClassDB::register_class<VisualShaderNodeIf>(); ClassDB::register_class<VisualShaderNodeSwitch>(); + ClassDB::register_class<VisualShaderNodeScalarSwitch>(); ClassDB::register_class<VisualShaderNodeFresnel>(); ClassDB::register_class<VisualShaderNodeExpression>(); ClassDB::register_class<VisualShaderNodeGlobalExpression>(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 1a5f57ce48..f68dc9af38 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -611,6 +611,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("title_font", "GraphNode", default_font); theme->set_color("title_color", "GraphNode", Color(0, 0, 0, 1)); theme->set_color("close_color", "GraphNode", Color(0, 0, 0, 1)); + theme->set_color("resizer_color", "GraphNode", Color(0, 0, 0, 1)); theme->set_constant("title_offset", "GraphNode", 20 * scale); theme->set_constant("close_offset", "GraphNode", 18 * scale); theme->set_constant("port_offset", "GraphNode", 3 * scale); @@ -656,7 +657,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("hseparation", "Tree", 4 * scale); theme->set_constant("vseparation", "Tree", 4 * scale); - theme->set_constant("guide_width", "Tree", 2 * scale); theme->set_constant("item_margin", "Tree", 12 * scale); theme->set_constant("button_margin", "Tree", 4 * scale); theme->set_constant("draw_relationship_lines", "Tree", 0); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index daa51aabd7..0de462d616 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -1772,7 +1772,7 @@ SpatialMaterial::TextureChannel SpatialMaterial::get_refraction_texture_channel( return refraction_texture_channel; } -RID SpatialMaterial::get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass) { +RID SpatialMaterial::get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard, bool p_billboard_y) { int version = 0; if (p_shaded) @@ -1785,6 +1785,10 @@ RID SpatialMaterial::get_material_rid_for_2d(bool p_shaded, bool p_transparent, version |= 8; if (p_double_sided) version |= 16; + if (p_billboard) + version |= 32; + if (p_billboard_y) + version |= 64; if (materials_for_2d[version].is_valid()) { return materials_for_2d[version]->get_rid(); @@ -1800,6 +1804,11 @@ RID SpatialMaterial::get_material_rid_for_2d(bool p_shaded, bool p_transparent, material->set_flag(FLAG_SRGB_VERTEX_COLOR, true); material->set_flag(FLAG_ALBEDO_FROM_VERTEX_COLOR, true); material->set_flag(FLAG_USE_ALPHA_SCISSOR, p_cut_alpha); + if (p_billboard) { + material->set_billboard_mode(BILLBOARD_ENABLED); + } else if (p_billboard_y) { + material->set_billboard_mode(BILLBOARD_FIXED_Y); + } materials_for_2d[version] = material; diff --git a/scene/resources/material.h b/scene/resources/material.h index 4c368b3f8b..2423d6d48b 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -437,9 +437,7 @@ private: _FORCE_INLINE_ void _validate_feature(const String &text, Feature feature, PropertyInfo &property) const; - enum { - MAX_MATERIALS_FOR_2D = 32 - }; + static const int MAX_MATERIALS_FOR_2D = 128; static Ref<SpatialMaterial> materials_for_2d[MAX_MATERIALS_FOR_2D]; //used by Sprite3D and other stuff @@ -626,7 +624,7 @@ public: static void finish_shaders(); static void flush_changes(); - static RID get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass); + static RID get_material_rid_for_2d(bool p_shaded, bool p_transparent, bool p_double_sided, bool p_cut_alpha, bool p_opaque_prepass, bool p_billboard = false, bool p_billboard_y = false); RID get_shader_rid() const; diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 7cd765fb5d..0c39c3cbb1 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -165,11 +165,12 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf e.vertex[0] = edge.x; e.vertex[1] = edge.y; edges.push_back(e); + v[j]->edges.push_back(face.edges[j]); + v[(j + 1) % 3]->edges.push_back(face.edges[j]); } edges.write[face.edges[j]].faces.push_back(fidx); v[j]->faces.push_back(fidx); - v[j]->edges.push_back(face.edges[j]); } faces.push_back(face); diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index cd229732ba..1c41f30a94 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -1713,6 +1713,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } if (groups.size()) { + groups.sort_custom<StringName::AlphCompare>(); String sgroups = " groups=[\n"; for (int j = 0; j < groups.size(); j++) { sgroups += "\"" + String(groups[j]).c_escape() + "\",\n"; diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp new file mode 100644 index 0000000000..8446e2f249 --- /dev/null +++ b/scene/resources/skin.cpp @@ -0,0 +1,102 @@ +#include "skin.h" + +void Skin::set_bind_count(int p_size) { + ERR_FAIL_COND(p_size < 0); + binds.resize(p_size); + binds_ptr = binds.ptrw(); + bind_count = p_size; + emit_changed(); +} + +void Skin::add_bind(int p_bone, const Transform &p_pose) { + uint32_t index = bind_count; + set_bind_count(bind_count + 1); + set_bind_bone(index, p_bone); + set_bind_pose(index, p_pose); +} + +void Skin::set_bind_bone(int p_index, int p_bone) { + ERR_FAIL_INDEX(p_index, bind_count); + binds_ptr[p_index].bone = p_bone; + emit_changed(); +} + +void Skin::set_bind_pose(int p_index, const Transform &p_pose) { + ERR_FAIL_INDEX(p_index, bind_count); + binds_ptr[p_index].pose = p_pose; + emit_changed(); +} + +void Skin::clear_binds() { + binds.clear(); + binds_ptr = nullptr; + bind_count = 0; + emit_changed(); +} + +bool Skin::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + if (name == "bind_count") { + set_bind_count(p_value); + return true; + } else if (name.begins_with("bind/")) { + int index = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + if (what == "bone") { + set_bind_bone(index, p_value); + return true; + } else if (what == "pose") { + set_bind_pose(index, p_value); + return true; + } + } + return false; +} + +bool Skin::_get(const StringName &p_name, Variant &r_ret) const { + + String name = p_name; + if (name == "bind_count") { + r_ret = get_bind_count(); + return true; + } else if (name.begins_with("bind/")) { + int index = name.get_slicec('/', 1).to_int(); + String what = name.get_slicec('/', 2); + if (what == "bone") { + r_ret = get_bind_bone(index); + return true; + } else if (what == "pose") { + r_ret = get_bind_pose(index); + return true; + } + } + return false; +} +void Skin::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); + for (int i = 0; i < get_bind_count(); i++) { + p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater")); + p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose")); + } +} + +void Skin::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_bind_count", "bind_count"), &Skin::set_bind_count); + ClassDB::bind_method(D_METHOD("get_bind_count"), &Skin::get_bind_count); + + ClassDB::bind_method(D_METHOD("add_bind", "bone", "pose"), &Skin::add_bind); + + ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose); + ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose); + + ClassDB::bind_method(D_METHOD("set_bind_bone", "bind_index", "bone"), &Skin::set_bind_bone); + ClassDB::bind_method(D_METHOD("get_bind_bone", "bind_index"), &Skin::get_bind_bone); + + ClassDB::bind_method(D_METHOD("clear_binds"), &Skin::clear_binds); +} + +Skin::Skin() { + bind_count = 0; + binds_ptr = nullptr; +} diff --git a/scene/resources/skin.h b/scene/resources/skin.h new file mode 100644 index 0000000000..a7e55bfa89 --- /dev/null +++ b/scene/resources/skin.h @@ -0,0 +1,54 @@ +#ifndef SKIN_H +#define SKIN_H + +#include "core/resource.h" + +class Skin : public Resource { + GDCLASS(Skin, Resource) + + struct Bind { + int bone; + Transform pose; + }; + + Vector<Bind> binds; + + Bind *binds_ptr; + int bind_count; + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _get_property_list(List<PropertyInfo> *p_list) const; + + static void _bind_methods(); + +public: + void set_bind_count(int p_size); + inline int get_bind_count() const { return bind_count; } + + void add_bind(int p_bone, const Transform &p_pose); + + void set_bind_bone(int p_index, int p_bone); + void set_bind_pose(int p_index, const Transform &p_pose); + + inline int get_bind_bone(int p_index) const { +#ifdef DEBUG_ENABLED + ERR_FAIL_INDEX_V(p_index, bind_count, -1); +#endif + return binds_ptr[p_index].bone; + } + + inline Transform get_bind_pose(int p_index) const { +#ifdef DEBUG_ENABLED + ERR_FAIL_INDEX_V(p_index, bind_count, Transform()); +#endif + return binds_ptr[p_index].pose; + } + + void clear_binds(); + + Skin(); +}; + +#endif // SKIN_H diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 5dd429fa75..4453032f67 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -713,6 +713,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { Vector<Point2> verts; Vector<int> indices; Vector<Color> colors; + Vector<Point2> uvs; //DRAW SHADOW if (draw_shadow) { @@ -799,9 +800,17 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { } } + //COMPUTE UV COORDINATES + Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0); + uvs.resize(verts.size()); + for (int i = 0; i < verts.size(); i++) { + uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width; + uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height; + } + //DRAWING VisualServer *vs = VisualServer::get_singleton(); - vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors); + vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs); } float StyleBoxFlat::get_style_margin(Margin p_margin) const { diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 699410719c..f5ea6adc85 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1083,7 +1083,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui } else if (defval.get_type() == Variant::BOOL) { bool val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); - code += "\nbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n"; + code += "\tbool " + inputs[i] + " = " + (val ? "true" : "false") + ";\n"; } else if (defval.get_type() == Variant::VECTOR3) { Vector3 val = defval; inputs[i] = "n_in" + itos(node) + "p" + itos(i); @@ -1399,6 +1399,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, // Spatial, Fragment { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, @@ -1423,6 +1424,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_BOOLEAN, "front_facing", "FRONT_FACING" }, // Spatial, Light { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" }, @@ -1444,6 +1447,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = { { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "inv_projection", "INV_PROJECTION_MATRIX" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" }, { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(VIEWPORT_SIZE, 0.0)" }, + { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" }, // Canvas Item, Vertex { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX,0.0)" }, { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV,0.0)" }, @@ -2463,8 +2467,10 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad if (pre_symbols.empty()) { pre_symbols.push_back("\t"); pre_symbols.push_back(","); + pre_symbols.push_back(";"); pre_symbols.push_back("{"); pre_symbols.push_back("["); + pre_symbols.push_back("]"); pre_symbols.push_back("("); pre_symbols.push_back(" "); pre_symbols.push_back("-"); @@ -2479,11 +2485,12 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad static Vector<String> post_symbols; if (post_symbols.empty()) { - post_symbols.push_back("\0"); post_symbols.push_back("\t"); post_symbols.push_back("\n"); + post_symbols.push_back(","); post_symbols.push_back(";"); post_symbols.push_back("}"); + post_symbols.push_back("["); post_symbols.push_back("]"); post_symbols.push_back(")"); post_symbols.push_back(" "); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 9195d80cfc..b7173b157e 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "visual_shader_nodes.h" + ////////////// Scalar String VisualShaderNodeScalarConstant::get_caption() const { @@ -38,9 +39,11 @@ String VisualShaderNodeScalarConstant::get_caption() const { int VisualShaderNodeScalarConstant::get_input_port_count() const { return 0; } + VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarConstant::get_input_port_name(int p_port) const { return String(); } @@ -48,9 +51,11 @@ String VisualShaderNodeScalarConstant::get_input_port_name(int p_port) const { int VisualShaderNodeScalarConstant::get_output_port_count() const { return 1; } + VisualShaderNodeScalarConstant::PortType VisualShaderNodeScalarConstant::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarConstant::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -158,9 +163,11 @@ String VisualShaderNodeColorConstant::get_caption() const { int VisualShaderNodeColorConstant::get_input_port_count() const { return 0; } + VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { return String(); } @@ -168,9 +175,11 @@ String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const { int VisualShaderNodeColorConstant::get_output_port_count() const { return 2; } + VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const { return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port } @@ -222,9 +231,11 @@ String VisualShaderNodeVec3Constant::get_caption() const { int VisualShaderNodeVec3Constant::get_input_port_count() const { return 0; } + VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const { return String(); } @@ -232,9 +243,11 @@ String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const { int VisualShaderNodeVec3Constant::get_output_port_count() const { return 1; } + VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -280,9 +293,11 @@ String VisualShaderNodeTransformConstant::get_caption() const { int VisualShaderNodeTransformConstant::get_input_port_count() const { return 0; } + VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const { return String(); } @@ -290,9 +305,11 @@ String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const int VisualShaderNodeTransformConstant::get_output_port_count() const { return 1; } + VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_output_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } + String VisualShaderNodeTransformConstant::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -346,9 +363,11 @@ String VisualShaderNodeTexture::get_caption() const { int VisualShaderNodeTexture::get_input_port_count() const { return 2; } + VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_input_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeTexture::get_input_port_name(int p_port) const { return p_port == 0 ? "uv" : "lod"; } @@ -356,11 +375,13 @@ String VisualShaderNodeTexture::get_input_port_name(int p_port) const { int VisualShaderNodeTexture::get_output_port_count() const { return 2; } + VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(int p_port) const { if (p_port == 0 && source == SOURCE_DEPTH) return PORT_TYPE_SCALAR; return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeTexture::get_output_port_name(int p_port) const { if (p_port == 0 && source == SOURCE_DEPTH) return "depth"; @@ -632,9 +653,11 @@ String VisualShaderNodeCubeMap::get_caption() const { int VisualShaderNodeCubeMap::get_input_port_count() const { return 2; } + VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_input_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const { return p_port == 0 ? "uv" : "lod"; } @@ -642,9 +665,11 @@ String VisualShaderNodeCubeMap::get_input_port_name(int p_port) const { int VisualShaderNodeCubeMap::get_output_port_count() const { return 2; } + VisualShaderNodeCubeMap::PortType VisualShaderNodeCubeMap::get_output_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeCubeMap::get_output_port_name(int p_port) const { return p_port == 0 ? "rgb" : "alpha"; } @@ -735,6 +760,7 @@ void VisualShaderNodeCubeMap::_bind_methods() { VisualShaderNodeCubeMap::VisualShaderNodeCubeMap() { texture_type = TYPE_DATA; } + ////////////// Scalar Op String VisualShaderNodeScalarOp::get_caption() const { @@ -744,9 +770,11 @@ String VisualShaderNodeScalarOp::get_caption() const { int VisualShaderNodeScalarOp::get_input_port_count() const { return 2; } + VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarOp::get_input_port_name(int p_port) const { return p_port == 0 ? "a" : "b"; } @@ -754,9 +782,11 @@ String VisualShaderNodeScalarOp::get_input_port_name(int p_port) const { int VisualShaderNodeScalarOp::get_output_port_count() const { return 1; } + VisualShaderNodeScalarOp::PortType VisualShaderNodeScalarOp::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarOp::get_output_port_name(int p_port) const { return "op"; //no output port means the editor will be used as port } @@ -832,9 +862,11 @@ String VisualShaderNodeVectorOp::get_caption() const { int VisualShaderNodeVectorOp::get_input_port_count() const { return 2; } + VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const { return p_port == 0 ? "a" : "b"; } @@ -842,9 +874,11 @@ String VisualShaderNodeVectorOp::get_input_port_name(int p_port) const { int VisualShaderNodeVectorOp::get_output_port_count() const { return 1; } + VisualShaderNodeVectorOp::PortType VisualShaderNodeVectorOp::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorOp::get_output_port_name(int p_port) const { return "op"; //no output port means the editor will be used as port } @@ -924,9 +958,11 @@ String VisualShaderNodeColorOp::get_caption() const { int VisualShaderNodeColorOp::get_input_port_count() const { return 2; } + VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeColorOp::get_input_port_name(int p_port) const { return p_port == 0 ? "a" : "b"; } @@ -934,9 +970,11 @@ String VisualShaderNodeColorOp::get_input_port_name(int p_port) const { int VisualShaderNodeColorOp::get_output_port_count() const { return 1; } + VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeColorOp::get_output_port_name(int p_port) const { return "op"; //no output port means the editor will be used as port } @@ -1072,9 +1110,11 @@ String VisualShaderNodeTransformMult::get_caption() const { int VisualShaderNodeTransformMult::get_input_port_count() const { return 2; } + VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_input_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } + String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const { return p_port == 0 ? "a" : "b"; } @@ -1082,9 +1122,11 @@ String VisualShaderNodeTransformMult::get_input_port_name(int p_port) const { int VisualShaderNodeTransformMult::get_output_port_count() const { return 1; } + VisualShaderNodeTransformMult::PortType VisualShaderNodeTransformMult::get_output_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } + String VisualShaderNodeTransformMult::get_output_port_name(int p_port) const { return "mult"; //no output port means the editor will be used as port } @@ -1147,9 +1189,11 @@ String VisualShaderNodeTransformVecMult::get_caption() const { int VisualShaderNodeTransformVecMult::get_input_port_count() const { return 2; } + VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR; } + String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const { return p_port == 0 ? "a" : "b"; } @@ -1157,9 +1201,11 @@ String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const { int VisualShaderNodeTransformVecMult::get_output_port_count() const { return 1; } + VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -1221,9 +1267,11 @@ String VisualShaderNodeScalarFunc::get_caption() const { int VisualShaderNodeScalarFunc::get_input_port_count() const { return 1; } + VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarFunc::get_input_port_name(int p_port) const { return ""; } @@ -1231,9 +1279,11 @@ String VisualShaderNodeScalarFunc::get_input_port_name(int p_port) const { int VisualShaderNodeScalarFunc::get_output_port_count() const { return 1; } + VisualShaderNodeScalarFunc::PortType VisualShaderNodeScalarFunc::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -1350,9 +1400,11 @@ String VisualShaderNodeVectorFunc::get_caption() const { int VisualShaderNodeVectorFunc::get_input_port_count() const { return 1; } + VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const { return ""; } @@ -1360,9 +1412,11 @@ String VisualShaderNodeVectorFunc::get_input_port_name(int p_port) const { int VisualShaderNodeVectorFunc::get_output_port_count() const { return 1; } + VisualShaderNodeVectorFunc::PortType VisualShaderNodeVectorFunc::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -1675,9 +1729,11 @@ String VisualShaderNodeDotProduct::get_caption() const { int VisualShaderNodeDotProduct::get_input_port_count() const { return 2; } + VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const { return p_port == 0 ? "a" : "b"; } @@ -1685,9 +1741,11 @@ String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const { int VisualShaderNodeDotProduct::get_output_port_count() const { return 1; } + VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeDotProduct::get_output_port_name(int p_port) const { return "dot"; } @@ -1710,9 +1768,11 @@ String VisualShaderNodeVectorLen::get_caption() const { int VisualShaderNodeVectorLen::get_input_port_count() const { return 1; } + VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const { return ""; } @@ -1720,9 +1780,11 @@ String VisualShaderNodeVectorLen::get_input_port_name(int p_port) const { int VisualShaderNodeVectorLen::get_output_port_count() const { return 1; } + VisualShaderNodeVectorLen::PortType VisualShaderNodeVectorLen::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeVectorLen::get_output_port_name(int p_port) const { return "length"; } @@ -1745,7 +1807,7 @@ int VisualShaderNodeDeterminant::get_input_port_count() const { return 1; } -VisualShaderNodeScalarClamp::PortType VisualShaderNodeDeterminant::get_input_port_type(int p_port) const { +VisualShaderNodeDeterminant::PortType VisualShaderNodeDeterminant::get_input_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } @@ -1872,7 +1934,7 @@ int VisualShaderNodeVectorDerivativeFunc::get_output_port_count() const { return 1; } -VisualShaderNodeScalarDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const { +VisualShaderNodeVectorDerivativeFunc::PortType VisualShaderNodeVectorDerivativeFunc::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } @@ -2078,7 +2140,7 @@ int VisualShaderNodeOuterProduct::get_input_port_count() const { return 2; } -VisualShaderNodeFaceForward::PortType VisualShaderNodeOuterProduct::get_input_port_type(int p_port) const { +VisualShaderNodeOuterProduct::PortType VisualShaderNodeOuterProduct::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } @@ -2143,7 +2205,7 @@ int VisualShaderNodeVectorScalarStep::get_output_port_count() const { return 1; } -VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorScalarStep::get_output_port_type(int p_port) const { +VisualShaderNodeVectorScalarStep::PortType VisualShaderNodeVectorScalarStep::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } @@ -2234,7 +2296,7 @@ int VisualShaderNodeVectorSmoothStep::get_output_port_count() const { return 1; } -VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorSmoothStep::get_output_port_type(int p_port) const { +VisualShaderNodeVectorSmoothStep::PortType VisualShaderNodeVectorSmoothStep::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } @@ -2285,7 +2347,7 @@ int VisualShaderNodeVectorScalarSmoothStep::get_output_port_count() const { return 1; } -VisualShaderNodeVectorClamp::PortType VisualShaderNodeVectorScalarSmoothStep::get_output_port_type(int p_port) const { +VisualShaderNodeVectorScalarSmoothStep::PortType VisualShaderNodeVectorScalarSmoothStep::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } @@ -2540,6 +2602,7 @@ VisualShaderNodeVectorScalarMix::VisualShaderNodeVectorScalarMix() { } ////////////// Vector Compose + String VisualShaderNodeVectorCompose::get_caption() const { return "VectorCompose"; } @@ -2547,9 +2610,11 @@ String VisualShaderNodeVectorCompose::get_caption() const { int VisualShaderNodeVectorCompose::get_input_port_count() const { return 3; } + VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const { if (p_port == 0) { return "x"; @@ -2563,9 +2628,11 @@ String VisualShaderNodeVectorCompose::get_input_port_name(int p_port) const { int VisualShaderNodeVectorCompose::get_output_port_count() const { return 1; } + VisualShaderNodeVectorCompose::PortType VisualShaderNodeVectorCompose::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorCompose::get_output_port_name(int p_port) const { return "vec"; } @@ -2590,9 +2657,11 @@ String VisualShaderNodeTransformCompose::get_caption() const { int VisualShaderNodeTransformCompose::get_input_port_count() const { return 4; } + VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const { if (p_port == 0) { return "x"; @@ -2608,9 +2677,11 @@ String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const { int VisualShaderNodeTransformCompose::get_output_port_count() const { return 1; } + VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_output_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } + String VisualShaderNodeTransformCompose::get_output_port_name(int p_port) const { return "xform"; } @@ -2635,9 +2706,11 @@ String VisualShaderNodeVectorDecompose::get_caption() const { int VisualShaderNodeVectorDecompose::get_input_port_count() const { return 1; } + VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const { return "vec"; } @@ -2645,9 +2718,11 @@ String VisualShaderNodeVectorDecompose::get_input_port_name(int p_port) const { int VisualShaderNodeVectorDecompose::get_output_port_count() const { return 3; } + VisualShaderNodeVectorDecompose::PortType VisualShaderNodeVectorDecompose::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeVectorDecompose::get_output_port_name(int p_port) const { if (p_port == 0) { return "x"; @@ -2679,9 +2754,11 @@ String VisualShaderNodeTransformDecompose::get_caption() const { int VisualShaderNodeTransformDecompose::get_input_port_count() const { return 1; } + VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_input_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } + String VisualShaderNodeTransformDecompose::get_input_port_name(int p_port) const { return "xform"; } @@ -2689,9 +2766,11 @@ String VisualShaderNodeTransformDecompose::get_input_port_name(int p_port) const int VisualShaderNodeTransformDecompose::get_output_port_count() const { return 4; } + VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) const { if (p_port == 0) { return "x"; @@ -2726,9 +2805,11 @@ String VisualShaderNodeScalarUniform::get_caption() const { int VisualShaderNodeScalarUniform::get_input_port_count() const { return 0; } + VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_input_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarUniform::get_input_port_name(int p_port) const { return String(); } @@ -2736,9 +2817,11 @@ String VisualShaderNodeScalarUniform::get_input_port_name(int p_port) const { int VisualShaderNodeScalarUniform::get_output_port_count() const { return 1; } + VisualShaderNodeScalarUniform::PortType VisualShaderNodeScalarUniform::get_output_port_type(int p_port) const { return PORT_TYPE_SCALAR; } + String VisualShaderNodeScalarUniform::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } @@ -2746,6 +2829,7 @@ String VisualShaderNodeScalarUniform::get_output_port_name(int p_port) const { String VisualShaderNodeScalarUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { return "uniform float " + get_uniform_name() + ";\n"; } + String VisualShaderNodeScalarUniform::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { return "\t" + p_output_vars[0] + " = " + get_uniform_name() + ";\n"; } @@ -2803,9 +2887,11 @@ String VisualShaderNodeColorUniform::get_caption() const { int VisualShaderNodeColorUniform::get_input_port_count() const { return 0; } + VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { return String(); } @@ -2813,9 +2899,11 @@ String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const { int VisualShaderNodeColorUniform::get_output_port_count() const { return 2; } + VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const { return p_port == 0 ? "color" : "alpha"; //no output port means the editor will be used as port } @@ -2843,9 +2931,11 @@ String VisualShaderNodeVec3Uniform::get_caption() const { int VisualShaderNodeVec3Uniform::get_input_port_count() const { return 0; } + VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const { return String(); } @@ -2853,12 +2943,15 @@ String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const { int VisualShaderNodeVec3Uniform::get_output_port_count() const { return 1; } + VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } + String VisualShaderNodeVec3Uniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { return "uniform vec3 " + get_uniform_name() + ";\n"; } @@ -2879,9 +2972,11 @@ String VisualShaderNodeTransformUniform::get_caption() const { int VisualShaderNodeTransformUniform::get_input_port_count() const { return 0; } + VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const { return PORT_TYPE_VECTOR; } + String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const { return String(); } @@ -2889,12 +2984,15 @@ String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const { int VisualShaderNodeTransformUniform::get_output_port_count() const { return 1; } + VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_output_port_type(int p_port) const { return PORT_TYPE_TRANSFORM; } + String VisualShaderNodeTransformUniform::get_output_port_name(int p_port) const { return ""; //no output port means the editor will be used as port } + String VisualShaderNodeTransformUniform::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const { return "uniform mat4 " + get_uniform_name() + ";\n"; } @@ -2915,9 +3013,11 @@ String VisualShaderNodeTextureUniform::get_caption() const { int VisualShaderNodeTextureUniform::get_input_port_count() const { return 2; } + VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_input_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { return p_port == 0 ? "uv" : "lod"; } @@ -2925,9 +3025,11 @@ String VisualShaderNodeTextureUniform::get_input_port_name(int p_port) const { int VisualShaderNodeTextureUniform::get_output_port_count() const { return 2; } + VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeTextureUniform::get_output_port_name(int p_port) const { return p_port == 0 ? "rgb" : "alpha"; } @@ -2989,6 +3091,7 @@ void VisualShaderNodeTextureUniform::set_color_default(ColorDefault p_default) { color_default = p_default; emit_changed(); } + VisualShaderNodeTextureUniform::ColorDefault VisualShaderNodeTextureUniform::get_color_default() const { return color_default; } @@ -3125,9 +3228,11 @@ String VisualShaderNodeCubeMapUniform::get_caption() const { int VisualShaderNodeCubeMapUniform::get_input_port_count() const { return 2; } + VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_input_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const { return p_port == 0 ? "normal" : "lod"; } @@ -3135,9 +3240,11 @@ String VisualShaderNodeCubeMapUniform::get_input_port_name(int p_port) const { int VisualShaderNodeCubeMapUniform::get_output_port_count() const { return 2; } + VisualShaderNodeCubeMapUniform::PortType VisualShaderNodeCubeMapUniform::get_output_port_type(int p_port) const { return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR; } + String VisualShaderNodeCubeMapUniform::get_output_port_name(int p_port) const { return p_port == 0 ? "rgb" : "alpha"; } @@ -3227,7 +3334,7 @@ VisualShaderNodeIf::VisualShaderNodeIf() { ////////////// Switch String VisualShaderNodeSwitch::get_caption() const { - return "Switch"; + return "VectorSwitch"; } int VisualShaderNodeSwitch::get_input_port_count() const { @@ -3282,10 +3389,33 @@ String VisualShaderNodeSwitch::generate_code(Shader::Mode p_mode, VisualShader:: VisualShaderNodeSwitch::VisualShaderNodeSwitch() { set_input_port_default_value(0, false); - set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0)); set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); } +////////////// Switch(scalar) + +String VisualShaderNodeScalarSwitch::get_caption() const { + return "ScalarSwitch"; +} + +VisualShaderNodeScalarSwitch::PortType VisualShaderNodeScalarSwitch::get_input_port_type(int p_port) const { + if (p_port == 0) { + return PORT_TYPE_BOOLEAN; + } + return PORT_TYPE_SCALAR; +} + +VisualShaderNodeScalarSwitch::PortType VisualShaderNodeScalarSwitch::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +VisualShaderNodeScalarSwitch::VisualShaderNodeScalarSwitch() { + set_input_port_default_value(0, false); + set_input_port_default_value(1, 1.0); + set_input_port_default_value(2, 0.0); +} + ////////////// Fresnel String VisualShaderNodeFresnel::get_caption() const { diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index f7efa396dc..0c6e060353 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -1530,6 +1530,18 @@ public: VisualShaderNodeSwitch(); }; +class VisualShaderNodeScalarSwitch : public VisualShaderNodeSwitch { + GDCLASS(VisualShaderNodeScalarSwitch, VisualShaderNodeSwitch); + +public: + virtual String get_caption() const; + + virtual PortType get_input_port_type(int p_port) const; + virtual PortType get_output_port_type(int p_port) const; + + VisualShaderNodeScalarSwitch(); +}; + /////////////////////////////////////// /// FRESNEL /////////////////////////////////////// |