summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/canvas_item.cpp13
-rw-r--r--scene/2d/collision_object_2d.cpp7
-rw-r--r--scene/2d/line_builder.cpp2
-rw-r--r--scene/2d/navigation_polygon.cpp2
-rw-r--r--scene/2d/physics_body_2d.cpp10
-rw-r--r--scene/2d/tile_map.cpp1
-rw-r--r--scene/3d/collision_object.cpp3
-rw-r--r--scene/3d/mesh_instance.cpp37
-rw-r--r--scene/3d/mesh_instance.h7
-rw-r--r--scene/3d/physics_body.cpp21
-rw-r--r--scene/3d/physics_body.h1
-rw-r--r--scene/3d/skeleton.cpp303
-rw-r--r--scene/3d/skeleton.h62
-rw-r--r--scene/3d/sprite_3d.cpp21
-rw-r--r--scene/3d/sprite_3d.h3
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/animation/animation_tree.cpp2
-rw-r--r--scene/animation/animation_tree_player.cpp6
-rw-r--r--scene/animation/skeleton_ik.cpp2
-rw-r--r--scene/animation/tween.cpp6
-rw-r--r--scene/gui/button.cpp289
-rw-r--r--scene/gui/button.h6
-rw-r--r--scene/gui/color_picker.cpp2
-rw-r--r--scene/gui/control.cpp28
-rw-r--r--scene/gui/dialogs.cpp5
-rw-r--r--scene/gui/gradient_edit.cpp10
-rw-r--r--scene/gui/graph_edit.cpp10
-rw-r--r--scene/gui/graph_node.cpp3
-rw-r--r--scene/gui/line_edit.cpp252
-rw-r--r--scene/gui/line_edit.h16
-rw-r--r--scene/gui/option_button.cpp6
-rw-r--r--scene/gui/rich_text_effect.cpp122
-rw-r--r--scene/gui/rich_text_effect.h87
-rw-r--r--scene/gui/rich_text_label.cpp469
-rw-r--r--scene/gui/rich_text_label.h150
-rw-r--r--scene/gui/scroll_container.cpp21
-rw-r--r--scene/gui/spin_box.cpp21
-rw-r--r--scene/gui/tab_container.cpp14
-rw-r--r--scene/gui/tab_container.h3
-rw-r--r--scene/gui/text_edit.cpp209
-rw-r--r--scene/gui/text_edit.h12
-rw-r--r--scene/gui/texture_button.cpp16
-rw-r--r--scene/gui/tree.cpp23
-rw-r--r--scene/gui/tree.h2
-rw-r--r--scene/gui/viewport_container.cpp1
-rw-r--r--scene/main/http_request.cpp4
-rw-r--r--scene/main/node.cpp26
-rw-r--r--scene/main/node.h9
-rw-r--r--scene/main/scene_tree.cpp64
-rw-r--r--scene/main/scene_tree.h1
-rwxr-xr-xscene/main/timer.cpp3
-rw-r--r--scene/main/viewport.cpp14
-rw-r--r--scene/register_scene_types.cpp7
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/material.cpp11
-rw-r--r--scene/resources/material.h6
-rw-r--r--scene/resources/mesh_data_tool.cpp3
-rw-r--r--scene/resources/resource_format_text.cpp1
-rw-r--r--scene/resources/skin.cpp102
-rw-r--r--scene/resources/skin.h54
-rw-r--r--scene/resources/style_box.cpp11
-rw-r--r--scene/resources/visual_shader.cpp11
-rw-r--r--scene/resources/visual_shader_nodes.cpp146
-rw-r--r--scene/resources/visual_shader_nodes.h12
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
///////////////////////////////////////