summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/tile_map.cpp55
-rw-r--r--scene/3d/camera.cpp9
-rw-r--r--scene/3d/camera.h2
-rw-r--r--scene/3d/particles.cpp4
-rw-r--r--scene/3d/scenario_fx.cpp10
-rw-r--r--scene/3d/vehicle_body.cpp12
-rw-r--r--scene/3d/vehicle_body.h2
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/animation/animation_tree_player.cpp2
-rw-r--r--scene/gui/grid_container.cpp4
-rw-r--r--scene/gui/menu_button.cpp2
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp152
-rw-r--r--scene/gui/popup_menu.h17
-rw-r--r--scene/gui/rich_text_label.cpp44
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/text_edit.cpp518
-rw-r--r--scene/gui/text_edit.h170
-rw-r--r--scene/gui/texture_progress.cpp72
-rw-r--r--scene/gui/texture_progress.h12
-rw-r--r--scene/gui/video_player.cpp15
-rw-r--r--scene/main/node.cpp5
-rw-r--r--scene/main/scene_tree.cpp6
-rw-r--r--scene/main/viewport.cpp23
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/mesh.cpp2
27 files changed, 777 insertions, 371 deletions
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index b602839b99..c126dd8f6b 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -265,12 +265,18 @@ void TileMap::_update_dirty_quadrants() {
SceneTree *st = SceneTree::get_singleton();
Color debug_collision_color;
+ Color debug_navigation_color;
bool debug_shapes = st && st->is_debugging_collisions_hint();
if (debug_shapes) {
debug_collision_color = st->get_debug_collisions_color();
}
+ bool debug_navigation = st && st->is_debugging_navigation_hint();
+ if (debug_navigation) {
+ debug_navigation_color = st->get_debug_navigation_color();
+ }
+
while (dirty_quadrant_list.first()) {
Quadrant &q = *dirty_quadrant_list.first()->self();
@@ -497,6 +503,55 @@ void TileMap::_update_dirty_quadrants() {
np.id = pid;
np.xform = xform;
q.navpoly_ids[E->key()] = np;
+
+ if (debug_navigation) {
+ RID debug_navigation_item = vs->canvas_item_create();
+ vs->canvas_item_set_parent(debug_navigation_item, canvas_item);
+ vs->canvas_item_set_z_as_relative_to_parent(debug_navigation_item, false);
+ vs->canvas_item_set_z_index(debug_navigation_item, VS::CANVAS_ITEM_Z_MAX - 2); // Display one below collision debug
+
+ if (debug_navigation_item.is_valid()) {
+ PoolVector<Vector2> navigation_polygon_vertices = navpoly->get_vertices();
+ int vsize = navigation_polygon_vertices.size();
+
+ if (vsize > 2) {
+ Vector<Color> colors;
+ Vector<Vector2> vertices;
+ vertices.resize(vsize);
+ colors.resize(vsize);
+ {
+ PoolVector<Vector2>::Read vr = navigation_polygon_vertices.read();
+ for (int i = 0; i < vsize; i++) {
+ vertices[i] = vr[i];
+ colors[i] = debug_navigation_color;
+ }
+ }
+
+ Vector<int> indices;
+
+ for (int i = 0; i < navpoly->get_polygon_count(); i++) {
+ Vector<int> polygon = navpoly->get_polygon(i);
+
+ for (int j = 2; j < polygon.size(); j++) {
+
+ int kofs[3] = { 0, j - 1, j };
+ for (int k = 0; k < 3; k++) {
+
+ int idx = polygon[kofs[k]];
+ ERR_FAIL_INDEX(idx, vsize);
+ indices.push_back(idx);
+ }
+ }
+ }
+ Transform2D navxform;
+ navxform.set_origin(offset.floor());
+ _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s);
+
+ vs->canvas_item_set_transform(debug_navigation_item, navxform);
+ vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors);
+ }
+ }
+ }
}
}
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 6998b34cfd..9de189c158 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -201,7 +201,7 @@ void Camera::make_current() {
//get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this);
}
-void Camera::clear_current() {
+void Camera::clear_current(bool p_enable_next) {
current = false;
if (!is_inside_tree())
@@ -209,7 +209,10 @@ void Camera::clear_current() {
if (get_viewport()->get_camera() == this) {
get_viewport()->_camera_set(NULL);
- get_viewport()->_camera_make_next_current(this);
+
+ if (p_enable_next) {
+ get_viewport()->_camera_make_next_current(this);
+ }
}
}
@@ -439,7 +442,7 @@ void Camera::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective);
ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal);
ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current);
- ClassDB::bind_method(D_METHOD("clear_current"), &Camera::clear_current);
+ ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current);
ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform);
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index e2679870de..109bf3adc6 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -113,7 +113,7 @@ public:
void set_projection(Camera::Projection p_mode);
void make_current();
- void clear_current();
+ void clear_current(bool p_enable_next = true);
void set_current(bool p_current);
bool is_current() const;
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 8617bbc2f6..693b416f6d 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -849,9 +849,9 @@ void ParticlesMaterial::_update_shader() {
code += " vec4(1.250, -1.050, -0.203, 0.0),\n";
code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n";
if (color_ramp.is_valid()) {
- code += " COLOR = textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0) * hue_rot_mat;\n";
+ code += " COLOR = hue_rot_mat * textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0);\n";
} else {
- code += " COLOR = color_value * hue_rot_mat;\n";
+ code += " COLOR = hue_rot_mat * color_value;\n";
}
if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) {
code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n";
diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp
index 02768ac91f..d5bff676cb 100644
--- a/scene/3d/scenario_fx.cpp
+++ b/scene/3d/scenario_fx.cpp
@@ -79,7 +79,11 @@ Ref<Environment> WorldEnvironment::get_environment() const {
String WorldEnvironment::get_configuration_warning() const {
- if (/*!is_visible_in_tree() ||*/ !is_inside_tree() || !environment.is_valid())
+ if (!environment.is_valid()) {
+ return TTR("WorldEnvironment needs an Environment resource.");
+ }
+
+ if (/*!is_visible_in_tree() ||*/ !is_inside_tree())
return String();
List<Node *> nodes;
@@ -89,6 +93,10 @@ String WorldEnvironment::get_configuration_warning() const {
return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
}
+ if (environment.is_valid() && get_viewport() && !get_viewport()->get_camera() && environment->get_background() != Environment::BG_CANVAS) {
+ return TTR("This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set this environment's Background Mode to Canvas (for 2D scenes).");
+ }
+
return String();
}
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index ed3bde9504..b72665aa2b 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -524,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) {
//bilateral constraint between two dynamic objects
void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1,
- PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse) {
+ PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) {
real_t normalLenSqr = normal.length_squared();
//ERR_FAIL_COND( normalLenSqr < real_t(1.1));
@@ -582,8 +582,12 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec
rel_vel = normal.dot(vel);
- //TODO: move this into proper structure
- real_t contactDamping = real_t(0.4);
+ // !BAS! We had this set to 0.4, in bullet its 0.2
+ // real_t contactDamping = real_t(0.2);
+
+ // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based
+ // keeping in mind our anti roll factor
+ real_t contactDamping = s->get_step() / p_rollInfluence;
#define ONLY_USE_LINEAR_MASS
#ifdef ONLY_USE_LINEAR_MASS
real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass);
@@ -704,7 +708,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
_resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS,
wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS,
- m_axle[i], m_sideImpulse[i]);
+ m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence);
m_sideImpulse[i] *= sideFrictionStiffness2;
}
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
index 7810a42e8a..1ac3693cc4 100644
--- a/scene/3d/vehicle_body.h
+++ b/scene/3d/vehicle_body.h
@@ -168,7 +168,7 @@ class VehicleBody : public RigidBody {
btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse);
};
- void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse);
+ void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence);
real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint);
void _update_friction(PhysicsDirectBodyState *s);
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 04e7d5cc10..2cf488ade4 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -590,8 +590,8 @@ void AnimationPlayer::_animation_update_transforms() {
Transform t;
t.origin = nc->loc_accum;
- t.basis = nc->rot_accum;
t.basis.scale(nc->scale_accum);
+ t.basis.rotate(nc->rot_accum.get_euler());
if (nc->skeleton && nc->bone_idx >= 0) {
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 89f0e43a86..e811b7a7b3 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -895,13 +895,13 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
}
Transform xform;
- xform.basis = t.rot;
xform.origin = t.loc;
t.scale.x += 1.0;
t.scale.y += 1.0;
t.scale.z += 1.0;
xform.basis.scale(t.scale);
+ xform.basis.rotate(t.rot.get_euler());
if (t.bone_idx >= 0) {
if (t.skeleton)
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 9948672097..b401abd436 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -84,8 +84,8 @@ void GridContainer::_notification(int p_what) {
if (!row_expanded.has(E->key()))
remaining_space.height -= E->get();
}
- remaining_space.height -= vsep * (max_row - 1);
- remaining_space.width -= hsep * (max_col - 1);
+ remaining_space.height -= vsep * MAX(max_row - 1, 0);
+ remaining_space.width -= hsep * MAX(max_col - 1, 0);
bool can_fit = false;
while (!can_fit && col_expanded.size() > 0) {
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 2e74faa61d..87cf4dc334 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -59,7 +59,6 @@ void MenuButton::pressed() {
popup->set_size(Size2(size.width, 0));
popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size()));
popup->popup();
- popup->set_invalidate_click_until_motion();
}
void MenuButton::_gui_input(Ref<InputEvent> p_event) {
@@ -109,7 +108,6 @@ MenuButton::MenuButton() {
add_child(popup);
popup->set_as_toplevel(true);
popup->set_pass_on_modal_close_click(false);
- connect("button_up", popup, "call_deferred", make_binds("grab_click_focus"));
set_process_unhandled_key_input(true);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 6e53f11b99..aaad10f579 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -118,7 +118,7 @@ void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_lab
}
void OptionButton::add_item(const String &p_label, int p_ID) {
- popup->add_check_item(p_label, p_ID);
+ popup->add_radio_check_item(p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 747230e69f..9ff3bd6e81 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -55,7 +55,7 @@ Size2 PopupMenu::get_minimum_size() const {
float max_w = 0;
int font_h = font->get_height();
- int check_w = get_icon("checked")->get_width();
+ int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width());
int accel_max_w = 0;
for (int i = 0; i < items.size(); i++) {
@@ -74,7 +74,7 @@ Size2 PopupMenu::get_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable) {
+ if (items[i].checkable_type) {
size.width += check_w + hseparation;
}
@@ -284,7 +284,8 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (b->is_pressed())
return;
- switch (b->get_button_index()) {
+ int button_idx = b->get_button_index();
+ switch (button_idx) {
case BUTTON_WHEEL_DOWN: {
@@ -298,30 +299,37 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll(b->get_factor(), b->get_position());
}
} break;
- case BUTTON_LEFT: {
-
- int over = _get_mouse_over(b->get_position());
-
- if (invalidated_click) {
- invalidated_click = false;
- break;
- }
- if (over < 0) {
- hide();
- break; //non-activable
+ default: {
+ // Allow activating item by releasing the LMB or any that was down when the popup appeared
+ if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
+
+ bool was_during_grabbed_click = during_grabbed_click;
+ during_grabbed_click = false;
+
+ int over = _get_mouse_over(b->get_position());
+
+ if (invalidated_click) {
+ invalidated_click = false;
+ break;
+ }
+ if (over < 0) {
+ if (!was_during_grabbed_click) {
+ hide();
+ }
+ break; //non-activable
+ }
+
+ if (items[over].separator || items[over].disabled)
+ break;
+
+ if (items[over].submenu != "") {
+
+ _activate_submenu(over);
+ return;
+ }
+ activate_item(over);
}
-
- if (items[over].separator || items[over].disabled)
- break;
-
- if (items[over].submenu != "") {
-
- _activate_submenu(over);
- return;
- }
- activate_item(over);
-
- } break;
+ }
}
//update();
@@ -408,8 +416,9 @@ void PopupMenu::_notification(int p_what) {
Ref<StyleBox> style = get_stylebox("panel");
Ref<StyleBox> hover = get_stylebox("hover");
Ref<Font> font = get_font("font");
- Ref<Texture> check = get_icon("checked");
- Ref<Texture> uncheck = get_icon("unchecked");
+ // In Item::checkable_type enum order (less the non-checkable member)
+ Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") };
+ Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
@@ -452,14 +461,10 @@ void PopupMenu::_notification(int p_what) {
separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
}
- if (items[i].checkable) {
-
- if (items[i].checked)
- check->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0)));
- else
- uncheck->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0)));
-
- item_ofs.x += check->get_width() + hseparation;
+ if (items[i].checkable_type) {
+ Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
+ icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)));
+ item_ofs.x += icon->get_width() + hseparation;
}
if (!items[i].icon.is_null()) {
@@ -503,6 +508,11 @@ void PopupMenu::_notification(int p_what) {
update();
}
} break;
+ case NOTIFICATION_POST_POPUP: {
+
+ initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
+ during_grabbed_click = (bool)initial_button_mask;
+ } break;
case NOTIFICATION_POPUP_HIDE: {
if (mouse_over >= 0) {
@@ -554,10 +564,11 @@ void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+
void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
Item item;
@@ -565,11 +576,18 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_check_item(p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
@@ -598,6 +616,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g
items.push_back(item);
update();
}
+
void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
@@ -607,7 +626,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh
Item item;
item.ID = p_ID;
item.shortcut = p_shortcut;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
item.icon = p_icon;
item.shortcut_is_global = p_global;
items.push_back(item);
@@ -624,11 +643,18 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
item.ID = p_ID;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+
+ add_check_shortcut(p_shortcut, p_ID, p_global);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
Item item;
@@ -636,7 +662,6 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = false;
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
@@ -811,7 +836,14 @@ bool PopupMenu::is_item_separator(int p_idx) const {
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checkable = p_checkable;
+ items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
+ update();
+}
+
+void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+ items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
update();
}
@@ -867,7 +899,12 @@ void PopupMenu::toggle_item_multistate(int p_idx) {
bool PopupMenu::is_item_checkable(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
- return items[p_idx].checkable;
+ return items[p_idx].checkable_type;
+}
+
+bool PopupMenu::is_item_radio_checkable(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), false);
+ return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON;
}
int PopupMenu::get_item_count() const {
@@ -941,7 +978,7 @@ void PopupMenu::activate_item(int p_item) {
// We close all parents that are chained together,
// with hide_on_item_selection enabled
- if (items[p_item].checkable) {
+ if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection())
break;
} else if (0 < items[p_item].max_states) {
@@ -958,7 +995,7 @@ void PopupMenu::activate_item(int p_item) {
// Hides popup by default; unless otherwise specified
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
- if (items[p_item].checkable) {
+ if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection)
return;
} else if (0 < items[p_item].max_states) {
@@ -1010,7 +1047,9 @@ Array PopupMenu::_get_items() const {
items.push_back(get_item_text(i));
items.push_back(get_item_icon(i));
- items.push_back(is_item_checkable(i));
+ // For compatibility, use false/true for no/checkbox and integers for other values
+ int ct = this->items[i].checkable_type;
+ items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct));
items.push_back(is_item_checked(i));
items.push_back(is_item_disabled(i));
@@ -1053,7 +1092,9 @@ void PopupMenu::_set_items(const Array &p_items) {
String text = p_items[i + 0];
Ref<Texture> icon = p_items[i + 1];
+ // For compatibility, use false/true for no/checkbox and integers for other values
bool checkable = p_items[i + 2];
+ bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
bool checked = p_items[i + 3];
bool disabled = p_items[i + 4];
@@ -1066,7 +1107,13 @@ void PopupMenu::_set_items(const Array &p_items) {
int idx = get_item_count();
add_item(text, id);
set_item_icon(idx, icon);
- set_item_as_checkable(idx, checkable);
+ if (checkable) {
+ if (radio_checkable) {
+ set_item_as_radio_checkable(idx, true);
+ } else {
+ set_item_as_checkable(idx, true);
+ }
+ }
set_item_checked(idx, checked);
set_item_disabled(idx, disabled);
set_item_id(idx, id);
@@ -1147,12 +1194,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon);
@@ -1164,6 +1213,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu);
ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator);
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable);
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
@@ -1182,6 +1232,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu);
ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
@@ -1216,15 +1267,20 @@ void PopupMenu::_bind_methods() {
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
}
-void PopupMenu::set_invalidate_click_until_motion() {
+void PopupMenu::popup(const Rect2 &p_bounds) {
+
+ grab_click_focus();
moved = Vector2();
invalidated_click = true;
+ Popup::popup(p_bounds);
}
PopupMenu::PopupMenu() {
mouse_over = -1;
submenu_over = -1;
+ initial_button_mask = 0;
+ during_grabbed_click = false;
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 60f36e95ec..c7851969d0 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -46,7 +46,11 @@ class PopupMenu : public Popup {
String text;
String xl_text;
bool checked;
- bool checkable;
+ enum {
+ CHECKABLE_TYPE_NONE,
+ CHECKABLE_TYPE_CHECK_BOX,
+ CHECKABLE_TYPE_RADIO_BUTTON,
+ } checkable_type;
int max_states;
int state;
bool separator;
@@ -63,7 +67,7 @@ class PopupMenu : public Popup {
Item() {
checked = false;
- checkable = false;
+ checkable_type = CHECKABLE_TYPE_NONE;
separator = false;
max_states = 0;
state = 0;
@@ -78,6 +82,8 @@ class PopupMenu : public Popup {
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
+ int initial_button_mask;
+ bool during_grabbed_click;
int mouse_over;
int submenu_over;
Rect2 parent_rect;
@@ -115,12 +121,14 @@ public:
void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0);
@@ -134,6 +142,7 @@ public:
void set_item_submenu(int p_idx, const String &p_submenu);
void set_item_as_separator(int p_idx, bool p_separator);
void set_item_as_checkable(int p_idx, bool p_checkable);
+ void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable);
void set_item_tooltip(int p_idx, const String &p_tooltip);
void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false);
void set_item_h_offset(int p_idx, int p_offset);
@@ -154,6 +163,7 @@ public:
String get_item_submenu(int p_idx) const;
bool is_item_separator(int p_idx) const;
bool is_item_checkable(int p_idx) const;
+ bool is_item_radio_checkable(int p_idx) const;
String get_item_tooltip(int p_idx) const;
Ref<ShortCut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
@@ -178,7 +188,6 @@ public:
void add_autohide_area(const Rect2 &p_area);
void clear_autohide_areas();
- void set_invalidate_click_until_motion();
void set_hide_on_item_selection(bool p_enabled);
bool is_hide_on_item_selection() const;
@@ -188,6 +197,8 @@ public:
void set_hide_on_multistate_item_selection(bool p_enabled);
bool is_hide_on_multistate_item_selection() const;
+ virtual void popup(const Rect2 &p_bounds = Rect2());
+
PopupMenu();
~PopupMenu();
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 5bc5d8e690..6bfc4d4dee 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -125,6 +125,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
l.descent_caches.clear();
l.char_count = 0;
l.minimum_width = 0;
+ l.maximum_width = 0;
}
int wofs = margin;
@@ -200,7 +201,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
#define ENSURE_WIDTH(m_width) \
if (p_mode == PROCESS_CACHE) { \
- l.minimum_width = MAX(l.minimum_width, wofs + m_width); \
+ l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); \
+ l.minimum_width = MAX(l.minimum_width, m_width); \
} \
if (wofs + m_width > p_width) { \
if (p_mode == PROCESS_CACHE) { \
@@ -469,6 +471,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
//set minimums to zero
for (int i = 0; i < table->columns.size(); i++) {
table->columns[i].min_width = 0;
+ table->columns[i].max_width = 0;
table->columns[i].width = 0;
}
//compute minimum width for each cell
@@ -486,6 +489,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
_process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color());
table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width);
+ table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width);
}
idx++;
}
@@ -498,12 +502,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < table->columns.size(); i++) {
remaining_width -= table->columns[i].min_width;
+ if (table->columns[i].max_width > table->columns[i].min_width)
+ table->columns[i].expand = true;
if (table->columns[i].expand)
total_ratio += table->columns[i].expand_ratio;
}
//assign actual widths
-
for (int i = 0; i < table->columns.size(); i++) {
table->columns[i].width = table->columns[i].min_width;
if (table->columns[i].expand)
@@ -511,6 +516,39 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
table->total_width += table->columns[i].width + hseparation;
}
+ //resize to max_width if needed and distribute the remaining space
+ bool table_need_fit = true;
+ while (table_need_fit) {
+ table_need_fit = false;
+ //fit slim
+ for (int i = 0; i < table->columns.size(); i++) {
+ if (!table->columns[i].expand)
+ continue;
+ int dif = table->columns[i].width - table->columns[i].max_width;
+ if (dif > 0) {
+ table_need_fit = true;
+ table->columns[i].width = table->columns[i].max_width;
+ table->total_width -= dif;
+ total_ratio -= table->columns[i].expand_ratio;
+ }
+ }
+ //grow
+ remaining_width = available_width - table->total_width;
+ if (remaining_width > 0 && total_ratio > 0) {
+ for (int i = 0; i < table->columns.size(); i++) {
+ if (table->columns[i].expand) {
+ int dif = table->columns[i].max_width - table->columns[i].width;
+ if (dif > 0) {
+ int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
+ int incr = MIN(dif, slice);
+ table->columns[i].width += incr;
+ table->total_width += incr;
+ }
+ }
+ }
+ }
+ }
+
//compute caches properly again with the right width
idx = 0;
for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
@@ -1633,7 +1671,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
tag_stack.push_front(tag);
} else if (tag.begins_with("cell=")) {
- int ratio = tag.substr(6, tag.length()).to_int();
+ int ratio = tag.substr(5, tag.length()).to_int();
if (ratio < 1)
ratio = 1;
//use monospace font
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index e7d5e6bb1b..83938cff61 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -87,6 +87,7 @@ private:
int height_accum_cache;
int char_count;
int minimum_width;
+ int maximum_width;
Line() {
from = NULL;
@@ -199,6 +200,7 @@ private:
bool expand;
int expand_ratio;
int min_width;
+ int max_width;
int width;
};
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 48bd733e80..e214a020d5 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -145,6 +145,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
text[p_line].region_info.clear();
+ int ending_color_region = -1;
for (int i = 0; i < len; i++) {
if (!_is_symbol(str[i]))
@@ -184,6 +185,12 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
cri.region = j;
text[p_line].region_info[i] = cri;
i += lr - 1;
+
+ if (ending_color_region == -1 && !cr.line_only) {
+ ending_color_region = j;
+ } else if (ending_color_region == j) {
+ ending_color_region = -1;
+ }
break;
}
@@ -211,10 +218,16 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
cri.region = j;
text[p_line].region_info[i] = cri;
i += lr - 1;
+
+ if (ending_color_region == j) {
+ ending_color_region = -1;
+ }
+
break;
}
}
}
+ text[p_line].ending_color_region = ending_color_region;
}
const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) const {
@@ -619,44 +632,10 @@ void TextEdit::_notification(int p_what) {
Color color = cache.font_color;
color.a *= readonly_alpha;
- int in_region = -1;
-
if (syntax_coloring) {
-
if (cache.background_color.a > 0.01) {
-
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
}
- //compute actual region to start (may be inside say, a comment).
- //slow in very large documents :( but ok for source!
-
- for (int i = 0; i < cursor.line_ofs; i++) {
-
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i);
-
- if (in_region >= 0 && color_regions[in_region].line_only) {
- in_region = -1; //reset regions that end at end of line
- }
-
- for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
-
- const Text::ColorRegionInfo &cri = E->get();
-
- if (in_region == -1) {
-
- if (!cri.end) {
-
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
-
- if (cri.end || color_regions[cri.region].eq) {
-
- in_region = -1;
- }
- }
- }
- }
}
int brace_open_match_line = -1;
@@ -804,7 +783,6 @@ void TextEdit::_notification(int p_what) {
}
}
- int deregion = 0; //force it to clear inrgion
Point2 cursor_pos;
// get the highlighted words
@@ -848,19 +826,12 @@ void TextEdit::_notification(int p_what) {
if (smooth_scroll_enabled)
ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height());
- bool prev_is_char = false;
- bool prev_is_number = false;
- bool in_keyword = false;
bool underlined = false;
- bool in_word = false;
- bool in_function_name = false;
- bool in_member_variable = false;
- bool is_hex_notation = false;
- Color keyword_color;
// check if line contains highlighted word
int highlighted_text_col = -1;
int search_text_col = -1;
+ int highlighted_word_col = -1;
if (!search_text.empty())
search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
@@ -868,7 +839,11 @@ void TextEdit::_notification(int p_what) {
if (highlighted_text.length() != 0 && highlighted_text != search_text)
highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line);
+ if (select_identifiers_enabled && highlighted_word.length() != 0) {
+ if (_is_char(highlighted_word[0])) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ }
+ }
if (text.is_marked(line)) {
@@ -936,170 +911,28 @@ void TextEdit::_notification(int p_what) {
cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
}
- //loop through characters in one line
- for (int j = 0; j < str.length(); j++) {
-
- //look for keyword
-
- if (deregion > 0) {
- deregion--;
- if (deregion == 0)
- in_region = -1;
- }
- if (syntax_coloring && deregion == 0) {
-
- color = cache.font_color; //reset
- color.a *= readonly_alpha;
- //find keyword
- bool is_char = _is_text_char(str[j]);
- bool is_symbol = _is_symbol(str[j]);
- bool is_number = _is_number(str[j]);
-
- if (j == 0 && in_region >= 0 && color_regions[in_region].line_only) {
- in_region = -1; //reset regions that end at end of line
- }
-
- // allow ABCDEF in hex notation
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
- is_number = true;
- } else {
- is_hex_notation = false;
- }
-
- // check for dot or underscore or 'x' for hex notation in floating point number
- if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
- is_number = true;
- is_symbol = false;
- is_char = false;
-
- if (str[j] == 'x' && str[j - 1] == '0') {
- is_hex_notation = true;
- }
- }
-
- if (!in_word && _is_char(str[j]) && !is_number) {
- in_word = true;
- }
-
- if ((in_keyword || in_word) && !is_hex_notation) {
- is_number = false;
- }
-
- if (is_symbol && str[j] != '.' && in_word) {
- in_word = false;
- }
-
- if (is_symbol && cri_map.has(j)) {
-
- const Text::ColorRegionInfo &cri = cri_map[j];
-
- if (in_region == -1) {
-
- if (!cri.end) {
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
-
- if (cri.end || color_regions[cri.region].eq) {
-
- deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
- }
- }
- }
-
- if (!is_char) {
- in_keyword = false;
- underlined = false;
- }
-
- if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
-
- int to = j;
- while (to < str.length() && _is_text_char(str[to]))
- to++;
-
- uint32_t hash = String::hash(&str[j], to - j);
- StrRange range(&str[j], to - j);
-
- const Color *col = keywords.custom_getptr(range, hash);
-
- if (!col) {
- col = member_keywords.custom_getptr(range, hash);
-
- if (col) {
- for (int k = j - 1; k >= 0; k--) {
- if (str[k] == '.') {
- col = NULL; //member indexing not allowed
- break;
- } else if (str[k] > 32) {
- break;
- }
- }
- }
- }
-
- if (col) {
-
- in_keyword = true;
- keyword_color = *col;
- }
-
- if (select_identifiers_enabled && highlighted_word != String()) {
- if (highlighted_word == range) {
- underlined = true;
- }
- }
- }
-
- if (!in_function_name && in_word && !in_keyword) {
-
- int k = j;
- while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k++;
- }
-
- // check for space between name and bracket
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
- k++;
- }
-
- if (str[k] == '(') {
- in_function_name = true;
- }
- }
+ //loop through characters in one line
+ Map<int, HighlighterInfo> color_map;
+ if (syntax_coloring) {
+ color_map = _get_line_syntax_highlighting(line);
+ }
- if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
- int k = j;
- while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k--;
- }
+ // ensure we at least use the font color
+ Color current_color = cache.font_color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
+ }
+ for (int j = 0; j < str.length(); j++) {
- if (str[k] == '.') {
- in_member_variable = true;
+ if (syntax_coloring) {
+ if (color_map.has(j)) {
+ current_color = color_map[j].color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
}
}
-
- if (is_symbol) {
- in_function_name = false;
- in_member_variable = false;
- }
-
- if (in_region >= 0)
- color = color_regions[in_region].color;
- else if (in_keyword)
- color = keyword_color;
- else if (in_member_variable)
- color = cache.member_variable_color;
- else if (in_function_name)
- color = cache.function_color;
- else if (is_symbol)
- color = cache.symbol_color;
- else if (is_number)
- color = cache.number_color;
-
- prev_is_char = is_char;
- prev_is_number = is_number;
+ color = current_color;
}
int char_w;
@@ -1206,6 +1039,13 @@ void TextEdit::_notification(int p_what) {
}
}
+ if (highlighted_word_col != -1) {
+ if (j > highlighted_word_col + highlighted_word.length()) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ }
+ underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length());
+ }
+
if (brace_matching_enabled) {
if ((brace_open_match_line == line && brace_open_match_column == j) ||
(cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) {
@@ -1512,6 +1352,7 @@ void TextEdit::_notification(int p_what) {
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
}
+
} break;
case NOTIFICATION_FOCUS_ENTER: {
@@ -1528,7 +1369,6 @@ void TextEdit::_notification(int p_what) {
if (raised_from_completion) {
VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
}
-
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -4150,6 +3990,44 @@ void TextEdit::_update_caches() {
cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons");
cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons");
text.set_font(cache.font);
+
+ if (syntax_highlighter) {
+ syntax_highlighter->_update_cache();
+ }
+}
+
+SyntaxHighlighter *TextEdit::_get_syntax_highlighting() {
+ return syntax_highlighter;
+}
+
+void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter) {
+ syntax_highlighter = p_syntax_highlighter;
+ if (syntax_highlighter) {
+ syntax_highlighter->set_text_editor(this);
+ syntax_highlighter->_update_cache();
+ }
+ update();
+}
+
+int TextEdit::_get_line_ending_color_region(int p_line) const {
+ if (p_line < 0 || p_line > text.size() - 1) {
+ return -1;
+ }
+ return text.get_line_ending_color_region(p_line);
+}
+
+TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const {
+ if (p_region < 0 || p_region > color_regions.size()) {
+ return ColorRegion();
+ }
+ return color_regions[p_region];
+}
+
+Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const {
+ if (p_line < 0 || p_line > text.size() - 1) {
+ return Map<int, Text::ColorRegionInfo>();
+ }
+ return text.get_color_region_info(p_line);
}
void TextEdit::clear_colors() {
@@ -4165,6 +4043,14 @@ void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color)
update();
}
+bool TextEdit::has_keyword_color(String p_keyword) const {
+ return keywords.has(p_keyword);
+}
+
+Color TextEdit::get_keyword_color(String p_keyword) const {
+ return keywords[p_keyword];
+}
+
void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
@@ -4177,6 +4063,14 @@ void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color)
update();
}
+bool TextEdit::has_member_color(String p_member) const {
+ return member_keywords.has(p_member);
+}
+
+Color TextEdit::get_member_color(String p_member) const {
+ return member_keywords[p_member];
+}
+
void TextEdit::clear_member_keywords() {
member_keywords.clear();
update();
@@ -5691,6 +5585,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed);
ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color);
+ ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color);
+ ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color);
ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors);
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
@@ -5744,6 +5640,7 @@ TextEdit::TextEdit() {
clear();
wrap = false;
set_focus_mode(FOCUS_ALL);
+ syntax_highlighter = NULL;
_update_caches();
cache.size = Size2(1, 1);
cache.row_height = 1;
@@ -5860,3 +5757,216 @@ TextEdit::TextEdit() {
TextEdit::~TextEdit() {
}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int p_line) {
+ if (syntax_highlighter != NULL) {
+ return syntax_highlighter->_get_line_syntax_highlighting(p_line);
+ }
+
+ Map<int, HighlighterInfo> color_map;
+
+ bool prev_is_char = false;
+ bool prev_is_number = false;
+ bool in_keyword = false;
+ bool in_word = false;
+ bool in_function_name = false;
+ bool in_member_variable = false;
+ bool is_hex_notation = false;
+ Color keyword_color;
+ Color color;
+
+ int in_region = -1;
+ int deregion = 0;
+ for (int i = 0; i < p_line; i++) {
+ int ending_color_region = text.get_line_ending_color_region(i);
+ if (in_region == -1) {
+ in_region = ending_color_region;
+ } else if (in_region == ending_color_region) {
+ in_region = -1;
+ } else {
+ const Map<int, TextEdit::Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i);
+ for (const Map<int, TextEdit::Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
+ const TextEdit::Text::ColorRegionInfo &cri = E->get();
+ if (cri.region == in_region) {
+ in_region = -1;
+ }
+ }
+ }
+ }
+
+ const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line);
+ const String &str = text[p_line];
+ Color prev_color;
+ for (int j = 0; j < str.length(); j++) {
+ HighlighterInfo highlighter_info;
+
+ if (deregion > 0) {
+ deregion--;
+ if (deregion == 0) {
+ in_region = -1;
+ }
+ }
+
+ if (deregion != 0) {
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ continue;
+ }
+
+ color = cache.font_color;
+
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol = _is_symbol(str[j]);
+ bool is_number = _is_number(str[j]);
+
+ // allow ABCDEF in hex notation
+ if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ is_number = true;
+ } else {
+ is_hex_notation = false;
+ }
+
+ // check for dot or underscore or 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+ is_char = false;
+
+ if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j]) && !is_number) {
+ in_word = true;
+ }
+
+ if ((in_keyword || in_word) && !is_hex_notation) {
+ is_number = false;
+ }
+
+ if (is_symbol && str[j] != '.' && in_word) {
+ in_word = false;
+ }
+
+ if (is_symbol && cri_map.has(j)) {
+ const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
+
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
+ if (cri.end || color_regions[cri.region].eq) {
+ deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+
+ int to = j;
+ while (to < str.length() && _is_text_char(str[to]))
+ to++;
+
+ uint32_t hash = String::hash(&str[j], to - j);
+ StrRange range(&str[j], to - j);
+
+ const Color *col = keywords.custom_getptr(range, hash);
+
+ if (!col) {
+ col = member_keywords.custom_getptr(range, hash);
+
+ if (col) {
+ for (int k = j - 1; k >= 0; k--) {
+ if (str[k] == '.') {
+ col = NULL; //member indexing not allowed
+ break;
+ } else if (str[k] > 32) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (col) {
+ in_keyword = true;
+ keyword_color = *col;
+ }
+ }
+
+ if (!in_function_name && in_word && !in_keyword) {
+
+ int k = j;
+ while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
+
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ in_function_name = true;
+ }
+ }
+
+ if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ int k = j;
+ while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k--;
+ }
+
+ if (str[k] == '.') {
+ in_member_variable = true;
+ }
+ }
+
+ if (is_symbol) {
+ in_function_name = false;
+ in_member_variable = false;
+ }
+
+ if (in_region >= 0)
+ color = color_regions[in_region].color;
+ else if (in_keyword)
+ color = keyword_color;
+ else if (in_member_variable)
+ color = cache.member_variable_color;
+ else if (in_function_name)
+ color = cache.function_color;
+ else if (is_symbol)
+ color = cache.symbol_color;
+ else if (is_number)
+ color = cache.number_color;
+
+ prev_is_char = is_char;
+ prev_is_number = is_number;
+
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ }
+
+ return color_map;
+}
+
+void SyntaxHighlighter::set_text_editor(TextEdit *p_text_editor) {
+ text_editor = p_text_editor;
+}
+
+TextEdit *SyntaxHighlighter::get_text_editor() {
+ return text_editor;
+}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 8ac3b9fce6..2360ce79db 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -36,10 +36,84 @@
#include "scene/gui/scroll_bar.h"
#include "scene/main/timer.h"
+class SyntaxHighlighter;
+
class TextEdit : public Control {
- GDCLASS(TextEdit, Control);
+ GDCLASS(TextEdit, Control)
+
+public:
+ struct HighlighterInfo {
+ Color color;
+ };
+
+ struct ColorRegion {
+
+ Color color;
+ String begin_key;
+ String end_key;
+ bool line_only;
+ bool eq;
+ ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
+ begin_key = p_begin_key;
+ end_key = p_end_key;
+ color = p_color;
+ line_only = p_line_only || p_end_key == "";
+ eq = begin_key == end_key;
+ }
+ };
+
+ class Text {
+ public:
+ struct ColorRegionInfo {
+
+ int region;
+ bool end;
+ };
+
+ struct Line {
+ int width_cache : 24;
+ bool marked : 1;
+ bool breakpoint : 1;
+ bool hidden : 1;
+ int ending_color_region;
+ Map<int, ColorRegionInfo> region_info;
+ String data;
+ };
+
+ private:
+ const Vector<ColorRegion> *color_regions;
+ mutable Vector<Line> text;
+ Ref<Font> font;
+ int indent_size;
+
+ void _update_line_cache(int p_line) const;
+
+ public:
+ void set_indent_size(int p_indent_size);
+ void set_font(const Ref<Font> &p_font);
+ void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
+ int get_line_width(int p_line) const;
+ int get_max_width(bool p_exclude_hidden = false) const;
+ const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
+ void set(int p_line, const String &p_text);
+ void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
+ bool is_marked(int p_line) const { return text[p_line].marked; }
+ void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
+ bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
+ void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
+ bool is_hidden(int p_line) const { return text[p_line].hidden; }
+ int get_line_ending_color_region(int p_line) const { return text[p_line].ending_color_region; }
+ void insert(int p_at, const String &p_text);
+ void remove(int p_at);
+ int size() const { return text.size(); }
+ void clear();
+ void clear_caches();
+ _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
+ Text() { indent_size = 4; }
+ };
+private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
@@ -115,70 +189,6 @@ class TextEdit : public Control {
Size2 size;
} cache;
- struct ColorRegion {
-
- Color color;
- String begin_key;
- String end_key;
- bool line_only;
- bool eq;
- ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
- begin_key = p_begin_key;
- end_key = p_end_key;
- color = p_color;
- line_only = p_line_only || p_end_key == "";
- eq = begin_key == end_key;
- }
- };
-
- class Text {
- public:
- struct ColorRegionInfo {
-
- int region;
- bool end;
- };
-
- struct Line {
- int width_cache : 24;
- bool marked : 1;
- bool breakpoint : 1;
- bool hidden : 1;
- Map<int, ColorRegionInfo> region_info;
- String data;
- };
-
- private:
- const Vector<ColorRegion> *color_regions;
- mutable Vector<Line> text;
- Ref<Font> font;
- int indent_size;
-
- void _update_line_cache(int p_line) const;
-
- public:
- void set_indent_size(int p_indent_size);
- void set_font(const Ref<Font> &p_font);
- void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
- int get_line_width(int p_line) const;
- int get_max_width(bool p_exclude_hidden = false) const;
- const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
- void set(int p_line, const String &p_text);
- void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
- bool is_marked(int p_line) const { return text[p_line].marked; }
- void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
- bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
- void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
- bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text);
- void remove(int p_at);
- int size() const { return text.size(); }
- void clear();
- void clear_caches();
- _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
- Text() { indent_size = 4; }
- };
-
struct TextOperation {
enum Type {
@@ -209,9 +219,12 @@ class TextEdit : public Control {
void _do_text_op(const TextOperation &p_op, bool p_reverse);
//syntax coloring
+ SyntaxHighlighter *syntax_highlighter;
HashMap<String, Color> keywords;
HashMap<String, Color> member_keywords;
+ Map<int, HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+
Vector<ColorRegion> color_regions;
Set<String> completion_prefixes;
@@ -391,6 +404,13 @@ protected:
static void _bind_methods();
public:
+ SyntaxHighlighter *_get_syntax_highlighting();
+ void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter);
+
+ int _get_line_ending_color_region(int p_line) const;
+ ColorRegion _get_color_region(int p_region) const;
+ Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const;
+
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -545,10 +565,15 @@ public:
bool is_insert_mode() const;
void add_keyword_color(const String &p_keyword, const Color &p_color);
+ bool has_keyword_color(String p_keyword) const;
+ Color get_keyword_color(String p_keyword) const;
+
void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false);
void clear_colors();
void add_member_keyword(const String &p_keyword, const Color &p_color);
+ bool has_member_color(String p_member) const;
+ Color get_member_color(String p_member) const;
void clear_member_keywords();
int get_v_scroll() const;
@@ -621,4 +646,19 @@ public:
VARIANT_ENUM_CAST(TextEdit::MenuItems);
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
+class SyntaxHighlighter {
+protected:
+ TextEdit *text_editor;
+
+public:
+ virtual void _update_cache() = 0;
+ virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
+
+ virtual String get_name() = 0;
+ virtual List<String> get_supported_languages() = 0;
+
+ void set_text_editor(TextEdit *p_text_editor);
+ TextEdit *get_text_editor();
+};
+
#endif // TEXT_EDIT_H
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp
index 4b3ba6df3c..82d983184b 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress.cpp
@@ -106,6 +106,33 @@ Ref<Texture> TextureProgress::get_progress_texture() const {
return progress;
}
+void TextureProgress::set_tint_under(const Color &p_tint) {
+ tint_under = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_under() const {
+ return tint_under;
+}
+
+void TextureProgress::set_tint_progress(const Color &p_tint) {
+ tint_progress = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_progress() const {
+ return tint_progress;
+}
+
+void TextureProgress::set_tint_over(const Color &p_tint) {
+ tint_over = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_over() const {
+ return tint_over;
+}
+
Point2 TextureProgress::unit_val_to_uv(float val) {
if (progress.is_null())
return Point2();
@@ -170,7 +197,7 @@ Point2 TextureProgress::get_relative_center() {
return p;
}
-void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio) {
+void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) {
Vector2 texture_size = p_texture->get_size();
Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]);
Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]);
@@ -240,7 +267,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
}
RID ci = get_canvas_item();
- VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright);
+ VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, true, p_modulate);
}
void TextureProgress::_notification(int p_what) {
@@ -251,42 +278,42 @@ void TextureProgress::_notification(int p_what) {
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) {
if (under.is_valid()) {
- draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0);
+ draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under);
}
if (progress.is_valid()) {
- draw_nine_patch_stretched(progress, mode, get_as_ratio());
+ draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
}
if (over.is_valid()) {
- draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0);
+ draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over);
}
} else {
if (under.is_valid())
- draw_texture(under, Point2());
+ draw_texture(under, Point2(), tint_under);
if (progress.is_valid()) {
Size2 s = progress->get_size();
switch (mode) {
case FILL_LEFT_TO_RIGHT: {
Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_RIGHT_TO_LEFT: {
Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_TOP_TO_BOTTOM: {
Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_BOTTOM_TO_TOP: {
Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_CLOCKWISE:
case FILL_COUNTER_CLOCKWISE: {
float val = get_as_ratio() * rad_max_degrees / 360;
if (val == 1) {
Rect2 region = Rect2(Point2(), s);
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} else if (val != 0) {
Array pts;
float direction = mode == FILL_CLOCKWISE ? 1 : -1;
@@ -311,7 +338,9 @@ void TextureProgress::_notification(int p_what) {
uvs.push_back(uv);
points.push_back(Point2(uv.x * s.x, uv.y * s.y));
}
- draw_polygon(points, Vector<Color>(), uvs, progress);
+ Vector<Color> colors;
+ colors.push_back(tint_progress);
+ draw_polygon(points, colors, uvs, progress);
}
if (Engine::get_singleton()->is_editor_hint()) {
Point2 p = progress->get_size();
@@ -323,11 +352,11 @@ void TextureProgress::_notification(int p_what) {
}
} break;
default:
- draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)));
+ draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
}
}
if (over.is_valid())
- draw_texture(over, Point2());
+ draw_texture(over, Point2(), tint_over);
}
} break;
@@ -389,6 +418,15 @@ void TextureProgress::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode);
ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode);
+ ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under);
+ ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under);
+
+ ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress);
+ ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress);
+
+ ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over);
+ ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over);
+
ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle);
ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle);
@@ -409,6 +447,10 @@ void TextureProgress::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+ ADD_GROUP("Tint", "tint_");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_progress", "get_tint_progress");
ADD_GROUP("Radial Fill", "radial_");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees");
@@ -440,4 +482,6 @@ TextureProgress::TextureProgress() {
stretch_margin[MARGIN_RIGHT] = 0;
stretch_margin[MARGIN_BOTTOM] = 0;
stretch_margin[MARGIN_TOP] = 0;
+
+ tint_under = tint_progress = tint_over = Color(1, 1, 1);
}
diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h
index 77c3980e29..34158b5db5 100644
--- a/scene/gui/texture_progress.h
+++ b/scene/gui/texture_progress.h
@@ -82,6 +82,15 @@ public:
void set_nine_patch_stretch(bool p_stretch);
bool get_nine_patch_stretch() const;
+ void set_tint_under(const Color &p_tint);
+ Color get_tint_under() const;
+
+ void set_tint_progress(const Color &p_tint);
+ Color get_tint_progress() const;
+
+ void set_tint_over(const Color &p_tint);
+ Color get_tint_over() const;
+
Size2 get_minimum_size() const;
TextureProgress();
@@ -93,10 +102,11 @@ private:
Point2 rad_center_off;
bool nine_patch_stretch;
int stretch_margin[4];
+ Color tint_under, tint_progress, tint_over;
Point2 unit_val_to_uv(float val);
Point2 get_relative_center();
- void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio);
+ void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate);
};
VARIANT_ENUM_CAST(TextureProgress::FillMode);
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 4eee0126d8..88e1847533 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "video_player.h"
+#include "scene/scene_string_names.h"
#include "os/os.h"
#include "servers/audio_server.h"
@@ -159,11 +160,7 @@ void VideoPlayer::_notification(int p_notification) {
bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
- if (stream.is_null())
- return;
- if (paused)
- return;
- if (!playback->is_playing())
+ if (stream.is_null() || paused || !playback->is_playing())
return;
double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec());
@@ -174,7 +171,11 @@ void VideoPlayer::_notification(int p_notification) {
if (delta == 0)
return;
- playback->update(delta);
+ playback->update(delta); // playback->is_playing() returns false in the last video frame
+
+ if (!playback->is_playing()) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
} break;
@@ -467,6 +468,8 @@ void VideoPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream");
//ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index cf22383e36..28b4540573 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2502,7 +2502,10 @@ void Node::replace_by(Node *p_node, bool p_keep_data) {
Node *child = get_child(0);
remove_child(child);
- p_node->add_child(child);
+ if (!child->is_owned_by_parent()) {
+ // add the custom children to the p_node
+ p_node->add_child(child);
+ }
}
p_node->set_owner(owner);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 12c3da78bd..037331dec1 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -498,14 +498,14 @@ bool SceneTree::idle(float p_time) {
Size2 win_size = Size2(OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height);
if (win_size != last_screen_size) {
+ last_screen_size = win_size;
+ _update_root_rect();
+
if (use_font_oversampling) {
DynamicFontAtSize::font_oversampling = OS::get_singleton()->get_window_size().width / root->get_visible_rect().size.width;
DynamicFont::update_oversampling();
}
- last_screen_size = win_size;
- _update_root_rect();
-
emit_signal("screen_resized");
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 08fbf44469..45a969eeda 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -181,6 +181,7 @@ public:
Viewport::GUI::GUI() {
mouse_focus = NULL;
+ mouse_click_grabber = NULL;
mouse_focus_button = -1;
key_focus = NULL;
mouse_over = NULL;
@@ -2278,7 +2279,7 @@ List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
else
p_control->_modal_set_prev_focus_owner(0);
- if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus)) {
+ if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) {
Ref<InputEventMouseButton> mb;
mb.instance();
mb->set_position(gui.mouse_focus->get_local_mouse_position());
@@ -2300,9 +2301,22 @@ Control *Viewport::_gui_get_focus_owner() {
void Viewport::_gui_grab_click_focus(Control *p_control) {
+ gui.mouse_click_grabber = p_control;
+ call_deferred("_post_gui_grab_click_focus");
+}
+
+void Viewport::_post_gui_grab_click_focus() {
+
+ Control *focus_grabber = gui.mouse_click_grabber;
+ if (!focus_grabber) {
+ // Redundant grab requests were made
+ return;
+ }
+ gui.mouse_click_grabber = NULL;
+
if (gui.mouse_focus) {
- if (gui.mouse_focus == p_control)
+ if (gui.mouse_focus == focus_grabber)
return;
Ref<InputEventMouseButton> mb;
mb.instance();
@@ -2313,9 +2327,9 @@ void Viewport::_gui_grab_click_focus(Control *p_control) {
mb->set_position(click);
mb->set_button_index(gui.mouse_focus_button);
mb->set_pressed(false);
- gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
+ gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
- gui.mouse_focus = p_control;
+ gui.mouse_focus = focus_grabber;
gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse();
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
mb->set_position(click);
@@ -2648,6 +2662,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip);
ClassDB::bind_method(D_METHOD("_gui_remove_focus"), &Viewport::_gui_remove_focus);
+ ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus);
ClassDB::bind_method(D_METHOD("set_shadow_atlas_size", "size"), &Viewport::set_shadow_atlas_size);
ClassDB::bind_method(D_METHOD("get_shadow_atlas_size"), &Viewport::get_shadow_atlas_size);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 07bbd3f1fa..94e49033e0 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -248,6 +248,7 @@ private:
bool key_event_accepted;
Control *mouse_focus;
+ Control *mouse_click_grabber;
int mouse_focus_button;
Control *key_focus;
Control *mouse_over;
@@ -323,6 +324,7 @@ private:
bool _gui_control_has_focus(const Control *p_control);
void _gui_control_grab_focus(Control *p_control);
void _gui_grab_click_focus(Control *p_control);
+ void _post_gui_grab_click_focus();
void _gui_accept_event();
Control *_gui_get_focus_owner();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 3e244aa8f8..ea70797530 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -582,6 +582,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
+ theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
+ theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
theme->set_font("font", "PopupMenu", default_font);
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 949ba12a4c..b832ea1239 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -315,6 +315,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
}
}
+ ERR_FAIL_COND_V(arrays.size() != ARRAY_MAX, Ref<ArrayMesh>());
+
{
PoolVector<int>::Write ir;
PoolVector<int> indices = arrays[ARRAY_INDEX];