summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp460
-rw-r--r--scene/2d/animated_sprite.h81
-rw-r--r--scene/2d/area_2d.cpp10
-rw-r--r--scene/2d/camera_2d.cpp45
-rw-r--r--scene/2d/canvas_item.cpp27
-rw-r--r--scene/2d/canvas_modulate.cpp29
-rw-r--r--scene/2d/canvas_modulate.h2
-rw-r--r--scene/2d/collision_polygon_2d.cpp14
-rw-r--r--scene/2d/collision_polygon_2d.h2
-rw-r--r--scene/2d/collision_shape_2d.cpp13
-rw-r--r--scene/2d/collision_shape_2d.h2
-rw-r--r--scene/2d/light_2d.cpp18
-rw-r--r--scene/2d/light_2d.h2
-rw-r--r--scene/2d/light_occluder_2d.cpp16
-rw-r--r--scene/2d/light_occluder_2d.h2
-rw-r--r--scene/2d/navigation_polygon.cpp23
-rw-r--r--scene/2d/navigation_polygon.h2
-rw-r--r--scene/2d/node_2d.cpp48
-rw-r--r--scene/2d/node_2d.h5
-rw-r--r--scene/2d/parallax_layer.cpp10
-rw-r--r--scene/2d/parallax_layer.h1
-rw-r--r--scene/2d/particles_2d.cpp8
-rw-r--r--scene/2d/particles_2d.h2
-rw-r--r--scene/2d/path_2d.cpp13
-rw-r--r--scene/2d/path_2d.h2
-rw-r--r--scene/2d/physics_body_2d.cpp45
-rw-r--r--scene/2d/physics_body_2d.h10
-rw-r--r--scene/2d/polygon_2d.cpp27
-rw-r--r--scene/2d/polygon_2d.h4
-rw-r--r--scene/2d/ray_cast_2d.cpp2
-rw-r--r--scene/2d/remote_transform_2d.cpp11
-rw-r--r--scene/2d/remote_transform_2d.h2
-rw-r--r--scene/2d/sample_player_2d.cpp9
-rw-r--r--scene/2d/sample_player_2d.h2
-rw-r--r--scene/2d/sprite.cpp19
-rw-r--r--scene/2d/sprite.h2
-rw-r--r--scene/2d/tile_map.cpp9
-rw-r--r--scene/2d/visibility_notifier_2d.cpp34
-rw-r--r--scene/2d/visibility_notifier_2d.h3
-rw-r--r--scene/3d/area.cpp74
-rw-r--r--scene/3d/area.h14
-rw-r--r--scene/3d/body_shape.cpp13
-rw-r--r--scene/3d/body_shape.h3
-rw-r--r--scene/3d/bone_attachment.cpp1
-rw-r--r--scene/3d/collision_polygon.cpp13
-rw-r--r--scene/3d/collision_polygon.h2
-rw-r--r--scene/3d/listener.cpp167
-rw-r--r--scene/3d/listener.h53
-rw-r--r--scene/3d/mesh_instance.cpp87
-rw-r--r--scene/3d/mesh_instance.h5
-rw-r--r--scene/3d/navigation_mesh.cpp22
-rw-r--r--scene/3d/navigation_mesh.h2
-rw-r--r--scene/3d/physics_body.cpp77
-rw-r--r--scene/3d/physics_body.h14
-rw-r--r--scene/3d/ray_cast.cpp34
-rw-r--r--scene/3d/ray_cast.h9
-rw-r--r--scene/3d/scenario_fx.cpp27
-rw-r--r--scene/3d/scenario_fx.h2
-rw-r--r--scene/3d/spatial.cpp51
-rw-r--r--scene/3d/spatial.h7
-rw-r--r--scene/3d/spatial_sample_player.cpp11
-rw-r--r--scene/3d/spatial_sample_player.h1
-rw-r--r--scene/3d/spatial_stream_player.cpp4
-rw-r--r--scene/3d/sprite_3d.cpp14
-rw-r--r--scene/3d/sprite_3d.h3
-rw-r--r--scene/3d/vehicle_body.cpp8
-rw-r--r--scene/3d/vehicle_body.h1
-rw-r--r--scene/animation/animation_player.cpp6
-rw-r--r--scene/animation/animation_tree_player.cpp234
-rw-r--r--scene/animation/animation_tree_player.h15
-rw-r--r--scene/animation/tween.cpp13
-rw-r--r--scene/audio/sample_player.cpp8
-rw-r--r--scene/audio/sample_player.h2
-rw-r--r--scene/audio/stream_player.cpp6
-rw-r--r--scene/gui/base_button.cpp53
-rw-r--r--scene/gui/base_button.h11
-rw-r--r--scene/gui/box_container.cpp2
-rw-r--r--scene/gui/button.cpp5
-rw-r--r--scene/gui/button.h4
-rw-r--r--scene/gui/button_array.cpp27
-rw-r--r--scene/gui/control.cpp303
-rw-r--r--scene/gui/control.h18
-rw-r--r--scene/gui/dialogs.cpp89
-rw-r--r--scene/gui/dialogs.h7
-rw-r--r--scene/gui/file_dialog.cpp2
-rw-r--r--scene/gui/input_action.cpp125
-rw-r--r--scene/gui/input_action.h26
-rw-r--r--scene/gui/item_list.cpp231
-rw-r--r--scene/gui/item_list.h26
-rw-r--r--scene/gui/label.cpp8
-rw-r--r--scene/gui/line_edit.cpp122
-rw-r--r--scene/gui/line_edit.h28
-rw-r--r--scene/gui/link_button.cpp2
-rw-r--r--scene/gui/margin_container.cpp24
-rw-r--r--scene/gui/menu_button.cpp37
-rw-r--r--scene/gui/patch_9_frame.cpp38
-rw-r--r--scene/gui/patch_9_frame.h4
-rw-r--r--scene/gui/popup.cpp10
-rw-r--r--scene/gui/popup.h1
-rw-r--r--scene/gui/popup_menu.cpp196
-rw-r--r--scene/gui/popup_menu.h19
-rw-r--r--scene/gui/progress_bar.cpp4
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/slider.cpp1
-rw-r--r--scene/gui/tab_container.cpp4
-rw-r--r--scene/gui/tabs.cpp181
-rw-r--r--scene/gui/text_edit.cpp667
-rw-r--r--scene/gui/text_edit.h73
-rw-r--r--scene/gui/texture_frame.cpp46
-rw-r--r--scene/gui/texture_frame.h15
-rw-r--r--scene/gui/tree.cpp348
-rw-r--r--scene/gui/tree.h45
-rw-r--r--scene/gui/video_player.cpp6
-rw-r--r--scene/io/resource_format_image.cpp4
-rw-r--r--scene/main/canvas_layer.cpp50
-rw-r--r--scene/main/canvas_layer.h7
-rw-r--r--scene/main/http_request.cpp5
-rw-r--r--scene/main/node.cpp59
-rw-r--r--scene/main/node.h10
-rw-r--r--scene/main/scene_main_loop.cpp12
-rw-r--r--scene/main/scene_main_loop.h1
-rw-r--r--scene/main/timer.cpp26
-rw-r--r--scene/main/timer.h4
-rw-r--r--scene/main/viewport.cpp209
-rw-r--r--scene/main/viewport.h21
-rw-r--r--scene/register_scene_types.cpp23
-rw-r--r--scene/resources/bit_mask.h2
-rw-r--r--scene/resources/curve.cpp1
-rw-r--r--scene/resources/default_theme/default_theme.cpp284
-rw-r--r--scene/resources/default_theme/default_theme.h2
-rw-r--r--scene/resources/dynamic_font.cpp813
-rw-r--r--scene/resources/dynamic_font.h194
-rw-r--r--scene/resources/dynamic_font_stb.cpp527
-rw-r--r--scene/resources/dynamic_font_stb.h178
-rw-r--r--scene/resources/font.cpp239
-rw-r--r--scene/resources/font.h74
-rw-r--r--scene/resources/mesh.cpp5
-rw-r--r--scene/resources/mesh_library.cpp26
-rw-r--r--scene/resources/mesh_library.h5
-rw-r--r--scene/resources/packed_scene.cpp2
-rw-r--r--scene/resources/stb_truetype.h3267
-rw-r--r--scene/resources/style_box.cpp21
-rw-r--r--scene/resources/style_box.h4
-rw-r--r--scene/resources/surface_tool.cpp33
-rw-r--r--scene/resources/surface_tool.h2
-rw-r--r--scene/resources/theme.cpp9
-rw-r--r--scene/resources/theme.h2
-rw-r--r--scene/resources/world_2d.cpp10
-rw-r--r--scene/scene_string_names.cpp13
-rw-r--r--scene/scene_string_names.h11
150 files changed, 9876 insertions, 1110 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 1ed508aed3..c062a6d1fc 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -29,81 +29,227 @@
#include "animated_sprite.h"
#include "scene/scene_string_names.h"
#include "os/os.h"
+#include "scene/scene_string_names.h"
-void AnimatedSprite::edit_set_pivot(const Point2& p_pivot) {
- set_offset(p_pivot);
-}
-Point2 AnimatedSprite::edit_get_pivot() const {
- return get_offset();
-}
-bool AnimatedSprite::edit_has_pivot() const {
+////////////////////////////
+
- return true;
-}
+void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture>& p_frame, int p_at_pos) {
-void SpriteFrames::add_frame(const Ref<Texture>& p_frame,int p_at_pos) {
+ Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND(!E);
- if (p_at_pos>=0 && p_at_pos<frames.size())
- frames.insert(p_at_pos,p_frame);
+ if (p_at_pos>=0 && p_at_pos<E->get().frames.size())
+ E->get().frames.insert(p_at_pos,p_frame);
else
- frames.push_back(p_frame);
+ E->get().frames.push_back(p_frame);
emit_changed();
}
-int SpriteFrames::get_frame_count() const {
+int SpriteFrames::get_frame_count(const StringName &p_anim) const {
+ const Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND_V(!E,0);
- return frames.size();
+ return E->get().frames.size();
}
-void SpriteFrames::remove_frame(int p_idx) {
+void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
+
+ Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND(!E);
- frames.remove(p_idx);
+ E->get().frames.remove(p_idx);
emit_changed();
}
-void SpriteFrames::clear() {
+void SpriteFrames::clear(const StringName &p_anim) {
- frames.clear();
+ Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND(!E);
+
+ E->get().frames.clear();
emit_changed();
}
+void SpriteFrames::clear_all() {
-Array SpriteFrames::_get_frames() const {
+ animations.clear();
+ add_animation("default");
+}
+
+
+
+void SpriteFrames::add_animation(const StringName& p_anim) {
+
+ ERR_FAIL_COND(animations.has(p_anim));
+
+ animations[p_anim]=Anim();
+}
+
+bool SpriteFrames::has_animation(const StringName& p_anim) const{
+
+ return animations.has(p_anim);
+}
+void SpriteFrames::remove_animation(const StringName& p_anim){
+
+ animations.erase(p_anim);
+}
+
+void SpriteFrames::rename_animation(const StringName& p_prev,const StringName& p_next) {
+
+ ERR_FAIL_COND(!animations.has(p_prev));
+ ERR_FAIL_COND(animations.has(p_next));
+
+ Anim anim = animations[p_prev];
+ animations.erase(p_prev);
+ animations[p_next]=anim;
- Array arr;
- arr.resize(frames.size());
- for(int i=0;i<frames.size();i++)
- arr[i]=frames[i];
+}
+
+Vector<String> SpriteFrames::_get_animation_list() const {
+
+ Vector<String> ret;
+ List<StringName> al;
+ get_animation_list(&al);
+ for(List<StringName>::Element *E=al.front();E;E=E->next()) {
+
+ ret.push_back(E->get());
+ }
+
+ return ret;
+}
+
+void SpriteFrames::get_animation_list(List<StringName> *r_animations) const{
+
+ for (const Map<StringName,Anim>::Element *E=animations.front();E;E=E->next()) {
+ r_animations->push_back(E->key());
+ }
+}
+
+void SpriteFrames::set_animation_speed(const StringName& p_anim,float p_fps){
+
+ ERR_FAIL_COND(p_fps<0);
+ Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND(!E);
+ E->get().speed=p_fps;
+}
+float SpriteFrames::get_animation_speed(const StringName& p_anim) const{
+
+ const Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND_V(!E,0);
+ return E->get().speed;
+}
+
+void SpriteFrames::set_animation_loop(const StringName& p_anim,bool p_loop){
+ Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND(!E);
+ E->get().loop=p_loop;
+}
+bool SpriteFrames::get_animation_loop(const StringName& p_anim) const{
+ const Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND_V(!E,false);
+ return E->get().loop;
- return arr;
}
void SpriteFrames::_set_frames(const Array& p_frames) {
- frames.resize(p_frames.size());
- for(int i=0;i<frames.size();i++)
- frames[i]=p_frames[i];
+ clear_all();
+ Map<StringName,Anim>::Element *E=animations.find(SceneStringNames::get_singleton()->_default);
+ ERR_FAIL_COND(!E);
+
+ E->get().frames.resize(p_frames.size());
+ for(int i=0;i<E->get().frames.size();i++)
+ E->get().frames[i]=p_frames[i];
+
+}
+Array SpriteFrames::_get_frames() const {
+
+ return Array();
+}
+
+Array SpriteFrames::_get_animations() const {
+
+ Array anims;
+ for (Map<StringName,Anim>::Element *E=animations.front();E;E=E->next()) {
+ Dictionary d;
+ d["name"]=E->key();
+ d["speed"]=E->get().speed;
+ d["loop"]=E->get().loop;
+ Array frames;
+ for(int i=0;i<E->get().frames.size();i++) {
+ frames.push_back(E->get().frames[i]);
+ }
+ d["frames"]=frames;
+ anims.push_back(d);
+ }
+ return anims;
+}
+void SpriteFrames::_set_animations(const Array& p_animations) {
+
+ animations.clear();
+ for(int i=0;i<p_animations.size();i++) {
+
+ Dictionary d=p_animations[i];
+
+ ERR_CONTINUE(!d.has("name"));
+ ERR_CONTINUE(!d.has("speed"));
+ ERR_CONTINUE(!d.has("loop"));
+ ERR_CONTINUE(!d.has("frames"));
+
+ Anim anim;
+ anim.speed=d["speed"];
+ anim.loop=d["loop"];
+ Array frames=d["frames"];
+ for(int i=0;i<frames.size();i++) {
+
+ RES res = frames[i];
+ anim.frames.push_back(res);
+ }
+
+ animations[d["name"]]=anim;
+
+
+ }
}
void SpriteFrames::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("add_frame","frame","atpos"),&SpriteFrames::add_frame,DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("get_frame_count"),&SpriteFrames::get_frame_count);
- ObjectTypeDB::bind_method(_MD("get_frame","idx"),&SpriteFrames::get_frame);
- ObjectTypeDB::bind_method(_MD("set_frame","idx","txt"),&SpriteFrames::set_frame);
- ObjectTypeDB::bind_method(_MD("remove_frame","idx"),&SpriteFrames::remove_frame);
- ObjectTypeDB::bind_method(_MD("clear"),&SpriteFrames::clear);
+
+ ObjectTypeDB::bind_method(_MD("add_animation","anim"),&SpriteFrames::add_animation);
+ ObjectTypeDB::bind_method(_MD("has_animation","anim"),&SpriteFrames::has_animation);
+ ObjectTypeDB::bind_method(_MD("remove_animation","anim"),&SpriteFrames::remove_animation);
+ ObjectTypeDB::bind_method(_MD("rename_animation","anim","newname"),&SpriteFrames::rename_animation);
+
+ ObjectTypeDB::bind_method(_MD("set_animation_speed","anim","speed"),&SpriteFrames::set_animation_speed);
+ ObjectTypeDB::bind_method(_MD("get_animation_speed","anim"),&SpriteFrames::get_animation_speed);
+
+ ObjectTypeDB::bind_method(_MD("set_animation_loop","anim","loop"),&SpriteFrames::set_animation_loop);
+ ObjectTypeDB::bind_method(_MD("get_animation_loop","anim"),&SpriteFrames::get_animation_loop);
+
+ ObjectTypeDB::bind_method(_MD("add_frame","anim","frame","atpos"),&SpriteFrames::add_frame,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("get_frame_count","anim"),&SpriteFrames::get_frame_count);
+ ObjectTypeDB::bind_method(_MD("get_frame","anim","idx"),&SpriteFrames::get_frame);
+ ObjectTypeDB::bind_method(_MD("set_frame","anim","idx","txt"),&SpriteFrames::set_frame);
+ ObjectTypeDB::bind_method(_MD("remove_frame","anim","idx"),&SpriteFrames::remove_frame);
+ ObjectTypeDB::bind_method(_MD("clear","anim"),&SpriteFrames::clear);
+ ObjectTypeDB::bind_method(_MD("clear_all"),&SpriteFrames::clear_all);
ObjectTypeDB::bind_method(_MD("_set_frames"),&SpriteFrames::_set_frames);
ObjectTypeDB::bind_method(_MD("_get_frames"),&SpriteFrames::_get_frames);
- ADD_PROPERTYNZ( PropertyInfo(Variant::ARRAY,"frames",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_frames"),_SCS("_get_frames"));
+ ADD_PROPERTYNZ( PropertyInfo(Variant::ARRAY,"frames",PROPERTY_HINT_NONE,"",0),_SCS("_set_frames"),_SCS("_get_frames")); //compatibility
+
+ ObjectTypeDB::bind_method(_MD("_set_animations"),&SpriteFrames::_set_animations);
+ ObjectTypeDB::bind_method(_MD("_get_animations"),&SpriteFrames::_get_animations);
+
+ ADD_PROPERTYNZ( PropertyInfo(Variant::ARRAY,"animations",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_animations"),_SCS("_get_animations")); //compatibility
}
@@ -112,33 +258,146 @@ void SpriteFrames::_bind_methods() {
SpriteFrames::SpriteFrames() {
+ add_animation(SceneStringNames::get_singleton()->_default);
+
+}
+
+
+
+
+
+
+void AnimatedSprite::edit_set_pivot(const Point2& p_pivot) {
+ set_offset(p_pivot);
}
+Point2 AnimatedSprite::edit_get_pivot() const {
+ return get_offset();
+}
+bool AnimatedSprite::edit_has_pivot() const {
+ return true;
+}
+void AnimatedSprite::_validate_property(PropertyInfo& property) const {
+ if (!frames.is_valid())
+ return;
+ if (property.name=="animation") {
-//////////////////////////
+ property.hint=PROPERTY_HINT_ENUM;
+ List<StringName> names;
+ frames->get_animation_list(&names);
+ names.sort_custom<StringName::AlphCompare>();
+ bool current_found=false;
+
+ for (List<StringName>::Element *E=names.front();E;E=E->next()) {
+ if (E->prev()) {
+ property.hint_string+=",";
+ }
+
+ property.hint_string+=String(E->get());
+ if (animation==E->get()) {
+ current_found=true;
+ }
+ }
+
+ if (!current_found) {
+ if (property.hint_string==String()) {
+ property.hint_string=String(animation);
+ } else {
+ property.hint_string=String(animation)+","+property.hint_string;
+ }
+ }
+ }
+
+
+ if (property.name=="frame") {
+
+ property.hint=PROPERTY_HINT_RANGE;
+
+ if (frames->has_animation(animation)) {
+ property.hint_string="0,"+itos(frames->get_frame_count(animation)-1)+",1";
+ } else {
+ property.hint_string="0,0,0";
+ }
+ }
+
+}
void AnimatedSprite::_notification(int p_what) {
switch(p_what) {
+ case NOTIFICATION_PROCESS: {
+
+ if (frames.is_null())
+ return;
+ if (!frames->has_animation(animation))
+ return;
+ if (frame<0)
+ return;
+
+ float speed = frames->get_animation_speed(animation);
+ if (speed==0)
+ return; //do nothing
+
+ float remaining = get_process_delta_time();
+
+ while(remaining) {
+
+ if (timeout<=0) {
+
+ timeout=1.0/speed;
+
+ int fc = frames->get_frame_count(animation);
+ if (frame>=fc-1) {
+ if (frames->get_animation_loop(animation)) {
+ frame=0;
+ } else {
+ frame=fc-1;
+ }
+ } else {
+ frame++;
+ }
+
+ update();
+ _change_notify("frame");
+ }
+
+ float to_process = MIN(timeout,remaining);
+ remaining-=to_process;
+ timeout-=to_process;
+ }
+ } break;
case NOTIFICATION_DRAW: {
- if (frames.is_null())
+ if (frames.is_null()) {
+ print_line("no draw no faemos");
return;
+ }
- if (frame<0 || frame>=frames->get_frame_count())
+ if (frame<0) {
+ print_line("no draw frame <0");
return;
+ }
+
+ if (!frames->has_animation(animation)) {
+ print_line("no draw no anim: "+String(animation));
+ return;
+ }
+
+
- Ref<Texture> texture = frames->get_frame(frame);
- if (texture.is_null())
+ Ref<Texture> texture = frames->get_frame(animation,frame);
+ if (texture.is_null()) {
+ print_line("no draw texture is null");
return;
+ }
//print_line("DECIDED TO DRAW");
@@ -184,11 +443,16 @@ void AnimatedSprite::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
if (!frames.is_valid()) {
frame=0;
-
} else {
set_frame(frame);
}
+
+
+
+ _change_notify();
+ _reset_timeout();
update();
+ update_configuration_warning();
}
@@ -202,19 +466,29 @@ void AnimatedSprite::set_frame(int p_frame) {
if (!frames.is_valid()) {
return;
}
- if (p_frame>=frames->get_frame_count())
- p_frame=frames->get_frame_count()-1;
+
+ if (frames->has_animation(animation)) {
+ int limit = frames->get_frame_count(animation);
+ if (p_frame>=limit)
+ p_frame=limit-1;
+
+ }
+
if (p_frame<0)
p_frame=0;
+
if (frame==p_frame)
return;
frame=p_frame;
+ _reset_timeout();
update();
_change_notify("frame");
emit_signal(SceneStringNames::get_singleton()->frame_changed);
+
+
}
int AnimatedSprite::get_frame() const {
@@ -281,11 +555,13 @@ Color AnimatedSprite::get_modulate() const{
Rect2 AnimatedSprite::get_item_rect() const {
- if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) {
+ if (!frames.is_valid() || !frames->has_animation(animation) || frame<0 || frame>=frames->get_frame_count(animation)) {
return Node2D::get_item_rect();
}
- Ref<Texture> t = frames->get_frame(frame);
+ Ref<Texture> t;
+ if (animation)
+ t = frames->get_frame(animation,frame);
if (t.is_null())
return Node2D::get_item_rect();
Size2i s = t->get_size();
@@ -303,14 +579,101 @@ Rect2 AnimatedSprite::get_item_rect() const {
void AnimatedSprite::_res_changed() {
set_frame(frame);
+ _change_notify("frame");
+ _change_notify("animation");
update();
}
+void AnimatedSprite::_set_playing(bool p_playing) {
+
+ if (playing==p_playing)
+ return;
+ playing=p_playing;
+ _reset_timeout();
+ set_process(playing);
+}
+
+bool AnimatedSprite::_is_playing() const {
+
+ return playing;
+}
+
+void AnimatedSprite::play(const StringName& p_animation) {
+
+ if (p_animation)
+ set_animation(p_animation);
+ _set_playing(true);
+}
+
+void AnimatedSprite::stop(){
+
+ _set_playing(false);
+}
+
+bool AnimatedSprite::is_playing() const {
+
+ return is_processing();
+}
+
+void AnimatedSprite::_reset_timeout() {
+
+ if (!playing)
+ return;
+
+ if (frames.is_valid() && frames->has_animation(animation)) {
+ float speed = frames->get_animation_speed(animation);
+ if (speed>0) {
+ timeout=1.0/speed;
+ } else {
+ timeout=0;
+ }
+ } else {
+ timeout=0;
+ }
+
+}
+
+void AnimatedSprite::set_animation(const StringName& p_animation){
+
+ if (animation==p_animation)
+ return;
+
+ animation=p_animation;
+ _reset_timeout();
+ set_frame(0);
+ _change_notify();
+ update();
+}
+StringName AnimatedSprite::get_animation() const{
+
+ return animation;
+}
+
+String AnimatedSprite::get_configuration_warning() const {
+
+ if (frames.is_null()) {
+ return TTR("A SpriteFrames resource must be created or set in the 'Frames' property in order for AnimatedSprite to display frames.");
+ }
+
+ return String();
+}
+
void AnimatedSprite::_bind_methods() {
+
ObjectTypeDB::bind_method(_MD("set_sprite_frames","sprite_frames:SpriteFrames"),&AnimatedSprite::set_sprite_frames);
ObjectTypeDB::bind_method(_MD("get_sprite_frames:SpriteFrames"),&AnimatedSprite::get_sprite_frames);
+ ObjectTypeDB::bind_method(_MD("set_animation","animation"),&AnimatedSprite::set_animation);
+ ObjectTypeDB::bind_method(_MD("get_animation"),&AnimatedSprite::get_animation);
+
+ ObjectTypeDB::bind_method(_MD("_set_playing","playing"),&AnimatedSprite::_set_playing);
+ ObjectTypeDB::bind_method(_MD("_is_playing"),&AnimatedSprite::_is_playing);
+
+ ObjectTypeDB::bind_method(_MD("play","anim"),&AnimatedSprite::play,DEFVAL(StringName()));
+ ObjectTypeDB::bind_method(_MD("stop"),&AnimatedSprite::stop);
+ ObjectTypeDB::bind_method(_MD("is_playing"),&AnimatedSprite::is_playing);
+
ObjectTypeDB::bind_method(_MD("set_centered","centered"),&AnimatedSprite::set_centered);
ObjectTypeDB::bind_method(_MD("is_centered"),&AnimatedSprite::is_centered);
@@ -335,7 +698,9 @@ void AnimatedSprite::_bind_methods() {
ADD_SIGNAL(MethodInfo("frame_changed"));
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "frames",PROPERTY_HINT_RESOURCE_TYPE,"SpriteFrames"), _SCS("set_sprite_frames"),_SCS("get_sprite_frames"));
+ ADD_PROPERTY( PropertyInfo( Variant::STRING, "animation"), _SCS("set_animation"),_SCS("get_animation"));
ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "frame",PROPERTY_HINT_SPRITE_FRAME), _SCS("set_frame"),_SCS("get_frame"));
+ ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "playing"), _SCS("_set_playing"),_SCS("_is_playing"));
ADD_PROPERTYNO( PropertyInfo( Variant::BOOL, "centered"), _SCS("set_centered"),_SCS("is_centered"));
ADD_PROPERTYNZ( PropertyInfo( Variant::VECTOR2, "offset"), _SCS("set_offset"),_SCS("get_offset"));
ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "flip_h"), _SCS("set_flip_h"),_SCS("is_flipped_h"));
@@ -351,9 +716,10 @@ AnimatedSprite::AnimatedSprite() {
vflip=false;
frame=0;
-
-
+ playing=false;
+ animation="default";
modulate=Color(1,1,1,1);
+ timeout=0;
}
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index da4f1b99af..968cd9aa30 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -37,23 +37,69 @@ class SpriteFrames : public Resource {
OBJ_TYPE(SpriteFrames,Resource);
- Vector< Ref<Texture> > frames;
+ struct Anim {
+
+ float speed;
+ bool loop;
+ Vector< Ref<Texture> > frames;
+
+ Anim() { loop=true; speed=5; }
+ };
+
+ Map<StringName,Anim> animations;
Array _get_frames() const;
void _set_frames(const Array& p_frames);
+
+ Array _get_animations() const;
+ void _set_animations(const Array& p_animations);
+
+ Vector<String> _get_animation_list() const;
+
protected:
static void _bind_methods();
public:
+ void add_animation(const StringName& p_anim);
+ bool has_animation(const StringName& p_anim) const;
+ void remove_animation(const StringName& p_anim);
+ void rename_animation(const StringName& p_prev,const StringName& p_next);
+
+ void get_animation_list(List<StringName> *r_animations) const;
+
+ void set_animation_speed(const StringName& p_anim,float p_fps);
+ float get_animation_speed(const StringName& p_anim) const;
+
+ void set_animation_loop(const StringName& p_anim,bool p_loop);
+ bool get_animation_loop(const StringName& p_anim) const;
+
+ void add_frame(const StringName& p_anim,const Ref<Texture>& p_frame,int p_at_pos=-1);
+ int get_frame_count(const StringName& p_anim) const;
+ _FORCE_INLINE_ Ref<Texture> get_frame(const StringName& p_anim,int p_idx) const {
+
+ const Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND_V(!E,Ref<Texture>());
+ ERR_FAIL_COND_V(p_idx<0,Ref<Texture>());
+ if (p_idx>=E->get().frames.size())
+ return Ref<Texture>();
+
+ return E->get().frames[p_idx];
+ }
+
+ void set_frame(const StringName& p_anim,int p_idx,const Ref<Texture>& p_frame){
+ Map<StringName,Anim>::Element *E=animations.find(p_anim);
+ ERR_FAIL_COND(!E);
+ ERR_FAIL_COND(p_idx<0);
+ if (p_idx>=E->get().frames.size())
+ return;
+ E->get().frames[p_idx]=p_frame;
+ }
+ void remove_frame(const StringName& p_anim,int p_idx);
+ void clear(const StringName& p_anim);
+ void clear_all();
- void add_frame(const Ref<Texture>& p_frame,int p_at_pos=-1);
- int get_frame_count() const;
- _FORCE_INLINE_ Ref<Texture> get_frame(int p_idx) const { ERR_FAIL_INDEX_V(p_idx,frames.size(),Ref<Texture>()); return frames[p_idx]; }
- void set_frame(int p_idx,const Ref<Texture>& p_frame){ ERR_FAIL_INDEX(p_idx,frames.size()); frames[p_idx]=p_frame; }
- void remove_frame(int p_idx);
- void clear();
SpriteFrames();
@@ -66,21 +112,32 @@ class AnimatedSprite : public Node2D {
OBJ_TYPE(AnimatedSprite,Node2D);
Ref<SpriteFrames> frames;
+ bool playing;
+ StringName animation;
int frame;
bool centered;
Point2 offset;
+ float timeout;
+
bool hflip;
bool vflip;
Color modulate;
void _res_changed();
+
+ void _reset_timeout();
+ void _set_playing(bool p_playing);
+ bool _is_playing() const;
+
+
protected:
static void _bind_methods();
void _notification(int p_what);
+ virtual void _validate_property(PropertyInfo& property) const;
public:
@@ -92,6 +149,13 @@ public:
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
+ void play(const StringName& p_animation=StringName());
+ void stop();
+ bool is_playing() const;
+
+ void set_animation(const StringName& p_animation);
+ StringName get_animation() const;
+
void set_frame(int p_frame);
int get_frame() const;
@@ -112,8 +176,9 @@ public:
virtual Rect2 get_item_rect() const;
-
+ virtual String get_configuration_warning() const;
AnimatedSprite();
};
+
#endif // ANIMATED_SPRITE_H
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index 50a115174d..71728966fd 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -638,8 +638,8 @@ void Area2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_overlapping_bodies"),&Area2D::get_overlapping_bodies);
ObjectTypeDB::bind_method(_MD("get_overlapping_areas"),&Area2D::get_overlapping_areas);
- ObjectTypeDB::bind_method(_MD("overlaps_body:PhysicsBody2D","body"),&Area2D::overlaps_body);
- ObjectTypeDB::bind_method(_MD("overlaps_area:Area2D","area"),&Area2D::overlaps_area);
+ ObjectTypeDB::bind_method(_MD("overlaps_body","body"),&Area2D::overlaps_body);
+ ObjectTypeDB::bind_method(_MD("overlaps_area","area"),&Area2D::overlaps_area);
ObjectTypeDB::bind_method(_MD("_body_inout"),&Area2D::_body_inout);
ObjectTypeDB::bind_method(_MD("_area_inout"),&Area2D::_area_inout);
@@ -660,9 +660,9 @@ void Area2D::_bind_methods() {
ADD_PROPERTYNZ( PropertyInfo(Variant::BOOL,"gravity_point"),_SCS("set_gravity_is_point"),_SCS("is_gravity_a_point"));
ADD_PROPERTYNZ( PropertyInfo(Variant::REAL,"gravity_distance_scale", PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_gravity_distance_scale"),_SCS("get_gravity_distance_scale"));
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"gravity_vec"),_SCS("set_gravity_vector"),_SCS("get_gravity_vector"));
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-1024,1024,0.01"),_SCS("set_gravity"),_SCS("get_gravity"));
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"linear_damp",PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_linear_damp"),_SCS("get_linear_damp"));
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_damp",PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_angular_damp"),_SCS("get_angular_damp"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-1024,1024,0.001"),_SCS("set_gravity"),_SCS("get_gravity"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"linear_damp",PROPERTY_HINT_RANGE,"0,100,0.01"),_SCS("set_linear_damp"),_SCS("get_linear_damp"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_damp",PROPERTY_HINT_RANGE,"0,100,0.01"),_SCS("set_angular_damp"),_SCS("get_angular_damp"));
ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"priority",PROPERTY_HINT_RANGE,"0,128,1"),_SCS("set_priority"),_SCS("get_priority"));
ADD_PROPERTYNO( PropertyInfo(Variant::BOOL,"monitoring"),_SCS("set_enable_monitoring"),_SCS("is_monitoring_enabled"));
ADD_PROPERTYNO( PropertyInfo(Variant::BOOL,"monitorable"),_SCS("set_monitorable"),_SCS("is_monitorable"));
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 67c1733759..fd8a0ed0f3 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -37,8 +37,7 @@ void Camera2D::_update_scroll() {
return;
if (get_tree()->is_editor_hint()) {
- // update(); //will just be drawn
- //??
+ update(); //will just be drawn
return;
}
@@ -85,7 +84,7 @@ Matrix32 Camera2D::get_camera_transform() {
if (anchor_mode==ANCHOR_MODE_DRAG_CENTER) {
- if (h_drag_enabled) {
+ if (h_drag_enabled && !get_tree()->is_editor_hint()) {
camera_pos.x = MIN( camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT]));
camera_pos.x = MAX( camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_LEFT]));
} else {
@@ -97,7 +96,7 @@ Matrix32 Camera2D::get_camera_transform() {
}
}
- if (v_drag_enabled) {
+ if (v_drag_enabled && !get_tree()->is_editor_hint()) {
camera_pos.y = MIN( camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM]));
camera_pos.y = MAX( camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_TOP]));
@@ -118,7 +117,7 @@ Matrix32 Camera2D::get_camera_transform() {
- if (smoothing_enabled) {
+ if (smoothing_enabled && !get_tree()->is_editor_hint()) {
float c = smoothing*get_fixed_process_delta_time();
smoothed_camera_pos = ((camera_pos-smoothed_camera_pos)*c)+smoothed_camera_pos;
@@ -145,7 +144,7 @@ Matrix32 Camera2D::get_camera_transform() {
screen_offset = screen_offset.rotated(angle);
}
- Rect2 screen_rect(-screen_offset+ret_camera_pos,screen_size);
+ Rect2 screen_rect(-screen_offset+ret_camera_pos,screen_size*zoom);
if (screen_rect.pos.x + screen_rect.size.x > limit[MARGIN_RIGHT])
screen_rect.pos.x = limit[MARGIN_RIGHT] - screen_rect.size.x;
@@ -241,6 +240,10 @@ void Camera2D::_notification(int p_what) {
add_to_group(group_name);
add_to_group(canvas_group_name);
+ if(get_tree()->is_editor_hint()) {
+ set_fixed_process(false);
+ }
+
_update_scroll();
first=true;
@@ -258,6 +261,34 @@ void Camera2D::_notification(int p_what) {
viewport=NULL;
} break;
+ case NOTIFICATION_DRAW: {
+
+ if (!is_inside_tree() || !get_tree()->is_editor_hint())
+ break;
+
+ Color area_axis_color(0.5, 0.42, 0.87, 0.63);
+ float area_axis_width = 1;
+ if(current)
+ area_axis_width = 3;
+
+ Matrix32 inv_camera_transform = get_camera_transform().affine_inverse();
+ Size2 screen_size = get_viewport_rect().size;
+
+ Vector2 screen_endpoints[4]= {
+ inv_camera_transform.xform(Vector2(0, 0)),
+ inv_camera_transform.xform(Vector2(screen_size.width,0)),
+ inv_camera_transform.xform(Vector2(screen_size.width, screen_size.height)),
+ inv_camera_transform.xform(Vector2(0, screen_size.height))
+ };
+
+ Matrix32 inv_transform = get_transform().affine_inverse(); // undo global space
+ draw_set_transform(inv_transform.get_origin(), inv_transform.get_rotation(), inv_transform.get_scale());
+
+ for(int i=0;i<4;i++) {
+ draw_line(screen_endpoints[i], screen_endpoints[(i+1)%4], area_axis_color, area_axis_width);
+ }
+
+ } break;
}
}
@@ -383,7 +414,7 @@ void Camera2D::force_update_scroll() {
void Camera2D::set_follow_smoothing(float p_speed) {
smoothing=p_speed;
- if (smoothing>0)
+ if (smoothing>0 && !(is_inside_tree() && get_tree()->is_editor_hint()))
set_fixed_process(true);
else
set_fixed_process(false);
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 483feea5c4..0c5886f755 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -455,19 +455,14 @@ void CanvasItem::_enter_canvas() {
if ((!get_parent() || !get_parent()->cast_to<CanvasItem>()) || toplevel) {
Node *n = this;
- Viewport *viewport=NULL;
+
canvas_layer=NULL;
while(n) {
- if (n->cast_to<Viewport>()) {
-
- viewport = n->cast_to<Viewport>();
- break;
- }
- if (!canvas_layer && n->cast_to<CanvasLayer>()) {
-
- canvas_layer = n->cast_to<CanvasLayer>();
+ canvas_layer = n->cast_to<CanvasLayer>();
+ if (canvas_layer) {
+ break;
}
n=n->get_parent();
}
@@ -476,7 +471,7 @@ void CanvasItem::_enter_canvas() {
if (canvas_layer)
canvas=canvas_layer->get_world_2d()->get_canvas();
else
- canvas=viewport->find_world_2d()->get_canvas();
+ canvas=get_viewport()->find_world_2d()->get_canvas();
VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,canvas);
@@ -487,7 +482,9 @@ void CanvasItem::_enter_canvas() {
} else {
+
CanvasItem *parent = get_parent_item();
+ canvas_layer=parent->canvas_layer;
VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,parent->get_canvas_item());
parent->_queue_sort_children();
}
@@ -1085,9 +1082,9 @@ void CanvasItem::_bind_methods() {
ObjectTypeDB::bind_method(_MD("draw_texture_rect","texture:Texture","rect","tile","modulate","transpose"),&CanvasItem::draw_texture_rect,DEFVAL(Color(1,1,1)),DEFVAL(false));
ObjectTypeDB::bind_method(_MD("draw_texture_rect_region","texture:Texture","rect","src_rect","modulate","transpose"),&CanvasItem::draw_texture_rect_region,DEFVAL(Color(1,1,1)),DEFVAL(false));
ObjectTypeDB::bind_method(_MD("draw_style_box","style_box:StyleBox","rect"),&CanvasItem::draw_style_box);
- ObjectTypeDB::bind_method(_MD("draw_primitive","points","colors","uvs","texture:Texture","width"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref<Texture>()),DEFVAL(1.0));
- ObjectTypeDB::bind_method(_MD("draw_polygon","points","colors","uvs","texture:Texture"),&CanvasItem::draw_polygon,DEFVAL(Array()),DEFVAL(Ref<Texture>()));
- ObjectTypeDB::bind_method(_MD("draw_colored_polygon","points","color","uvs","texture:Texture"),&CanvasItem::draw_colored_polygon,DEFVAL(Array()),DEFVAL(Ref<Texture>()));
+ ObjectTypeDB::bind_method(_MD("draw_primitive","points","colors","uvs","texture:Texture","width"),&CanvasItem::draw_primitive,DEFVAL(Variant()),DEFVAL(1.0));
+ ObjectTypeDB::bind_method(_MD("draw_polygon","points","colors","uvs","texture:Texture"),&CanvasItem::draw_polygon,DEFVAL(Vector2Array()),DEFVAL(Variant()));
+ ObjectTypeDB::bind_method(_MD("draw_colored_polygon","points","color","uvs","texture:Texture"),&CanvasItem::draw_colored_polygon,DEFVAL(Vector2Array()),DEFVAL(Variant()));
ObjectTypeDB::bind_method(_MD("draw_string","font:Font","pos","text","modulate","clip_w"),&CanvasItem::draw_string,DEFVAL(Color(1,1,1)),DEFVAL(-1));
ObjectTypeDB::bind_method(_MD("draw_char","font:Font","pos","char","next","modulate"),&CanvasItem::draw_char,DEFVAL(Color(1,1,1)));
@@ -1176,12 +1173,10 @@ Matrix32 CanvasItem::get_viewport_transform() const {
return canvas_layer->get_transform();
}
- } else if (get_viewport()) {
+ } else {
return get_viewport()->get_final_transform() * get_viewport()->get_canvas_transform();
}
- return Matrix32();
-
}
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index 77203a7110..6a74cb1d91 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -5,19 +5,30 @@ void CanvasModulate::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_CANVAS) {
- if (is_visible())
+ if (is_visible()) {
VS::get_singleton()->canvas_set_modulate(get_canvas(),color);
+ add_to_group("_canvas_modulate_"+itos(get_canvas().get_id()));
+ }
+
+
+
} else if (p_what==NOTIFICATION_EXIT_CANVAS) {
- if (is_visible())
+ if (is_visible()) {
VS::get_singleton()->canvas_set_modulate(get_canvas(),Color(1,1,1,1));
+ remove_from_group("_canvas_modulate_"+itos(get_canvas().get_id()));
+ }
} else if (p_what==NOTIFICATION_VISIBILITY_CHANGED) {
if (is_visible()) {
VS::get_singleton()->canvas_set_modulate(get_canvas(),color);
+ add_to_group("_canvas_modulate_"+itos(get_canvas().get_id()));
} else {
VS::get_singleton()->canvas_set_modulate(get_canvas(),Color(1,1,1,1));
+ remove_from_group("_canvas_modulate_"+itos(get_canvas().get_id()));
}
+
+ update_configuration_warning();
}
}
@@ -42,6 +53,20 @@ Color CanvasModulate::get_color() const {
return color;
}
+String CanvasModulate::get_configuration_warning() const {
+
+ if (!is_visible() || !is_inside_tree())
+ return String();
+
+ List<Node*> nodes;
+ get_tree()->get_nodes_in_group("_canvas_modulate_"+itos(get_canvas().get_id()),&nodes);
+
+ if (nodes.size()>1) {
+ return TTR("Only one visible CanvasModulate is allowed per scene (or set of instanced scenes). The first created one will work, while the rest will be ignored.");
+ }
+
+ return String();
+}
CanvasModulate::CanvasModulate()
{
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
index a6894f29c2..0445db27af 100644
--- a/scene/2d/canvas_modulate.h
+++ b/scene/2d/canvas_modulate.h
@@ -16,6 +16,8 @@ public:
void set_color(const Color& p_color);
Color get_color() const;
+ String get_configuration_warning() const;
+
CanvasModulate();
~CanvasModulate();
};
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 2a40a6207d..544f0e2088 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -297,6 +297,20 @@ Vector2 CollisionPolygon2D::_get_shape_range() const {
return Vector2(shape_from,shape_to);
}
+String CollisionPolygon2D::get_configuration_warning() const {
+
+ if (!get_parent()->cast_to<CollisionObject2D>()) {
+ return TTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+ }
+
+ if (polygon.empty()) {
+ return TTR("An empty CollisionPolygon2D has no effect on collision.");
+
+ }
+
+ return String();
+}
+
void CollisionPolygon2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionPolygon2D::_add_to_collision_object);
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index b2bd4d189d..9c0e4e0c01 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -85,6 +85,8 @@ public:
int get_collision_object_first_shape() const { return shape_from; }
int get_collision_object_last_shape() const { return shape_to; }
+ virtual String get_configuration_warning() const;
+
CollisionPolygon2D();
};
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 405310450b..c737cf0faf 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -201,6 +201,19 @@ int CollisionShape2D::_get_update_shape_index() const{
return update_shape_index;
}
+String CollisionShape2D::get_configuration_warning() const {
+
+ if (!get_parent()->cast_to<CollisionObject2D>()) {
+ return TTR("CollisionShape2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape.");
+ }
+
+ if (!shape.is_valid()) {
+ return TTR("A shape must be provided for CollisionShape2D to function. Please create a shape resource for it!");
+ }
+
+ return String();
+}
+
void CollisionShape2D::_bind_methods() {
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index b14dad73ba..6f3f17d412 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -63,6 +63,8 @@ public:
int get_collision_object_shape_index() const { return _get_update_shape_index(); }
+ virtual String get_configuration_warning() const;
+
CollisionShape2D();
};
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 9715afeaa4..1cb34075bb 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -61,6 +61,8 @@ void Light2D::set_texture( const Ref<Texture>& p_texture) {
VS::get_singleton()->canvas_light_set_texture(canvas_light,texture->get_rid());
else
VS::get_singleton()->canvas_light_set_texture(canvas_light,RID());
+
+ update_configuration_warning();
}
Ref<Texture> Light2D::get_texture() const {
@@ -282,6 +284,16 @@ void Light2D::_notification(int p_what) {
}
+String Light2D::get_configuration_warning() const {
+
+ if (!texture.is_valid()) {
+ return TTR("A texture with the shape of the light must be supplied to the 'texture' property.");
+ }
+
+ return String();
+}
+
+
void Light2D::_bind_methods() {
@@ -345,11 +357,11 @@ void Light2D::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture"));
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_texture_offset"),_SCS("get_texture_offset"));
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"scale",PROPERTY_HINT_RANGE,"0.01,4096,0.01"),_SCS("set_texture_scale"),_SCS("get_texture_scale"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"scale",PROPERTY_HINT_RANGE,"0.01,50,0.01"),_SCS("set_texture_scale"),_SCS("get_texture_scale"));
ADD_PROPERTY( PropertyInfo(Variant::COLOR,"color"),_SCS("set_color"),_SCS("get_color"));
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"energy"),_SCS("set_energy"),_SCS("get_energy"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"energy",PROPERTY_HINT_RANGE,"0.01,100,0.01"),_SCS("set_energy"),_SCS("get_energy"));
ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Add,Sub,Mix,Mask"),_SCS("set_mode"),_SCS("get_mode"));
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"range/height"),_SCS("set_height"),_SCS("get_height"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"range/height",PROPERTY_HINT_RANGE,"-100,100,0.1"),_SCS("set_height"),_SCS("get_height"));
ADD_PROPERTY( PropertyInfo(Variant::INT,"range/z_min",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z_range_min"),_SCS("get_z_range_min"));
ADD_PROPERTY( PropertyInfo(Variant::INT,"range/z_max",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z_range_max"),_SCS("get_z_range_max"));
ADD_PROPERTY( PropertyInfo(Variant::INT,"range/layer_min",PROPERTY_HINT_RANGE,"-512,512,1"),_SCS("set_layer_range_min"),_SCS("get_layer_range_min"));
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index ca437769e7..a8b0ef3b23 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -104,6 +104,8 @@ public:
virtual Rect2 get_item_rect() const;
+ String get_configuration_warning() const;
+
Light2D();
~Light2D();
};
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index d98bed0ea3..ce617b1737 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -45,6 +45,8 @@ RID OccluderPolygon2D::get_rid() const {
return occ_polygon;
}
+
+
void OccluderPolygon2D::_bind_methods() {
@@ -178,6 +180,20 @@ int LightOccluder2D::get_occluder_light_mask() const{
return mask;
}
+
+String LightOccluder2D::get_configuration_warning() const {
+
+ if (!occluder_polygon.is_valid()) {
+ return TTR("An occluder polygon must be set (or drawn) for this occluder to take effect.");
+ }
+
+ if (occluder_polygon.is_valid() && occluder_polygon->get_polygon().size()==0) {
+ return TTR("The occluder polygon for this occluder is empty. Please draw a polygon!");
+ }
+
+ return String();
+}
+
void LightOccluder2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_occluder_polygon","polygon:OccluderPolygon2D"),&LightOccluder2D::set_occluder_polygon);
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 0343e3697e..ccc2a1cd9c 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -66,6 +66,8 @@ public:
void set_occluder_light_mask(int p_mask);
int get_occluder_light_mask() const;
+ String get_configuration_warning() const;
+
LightOccluder2D();
~LightOccluder2D();
};
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
index 376aeb2d85..8c0d9cf35f 100644
--- a/scene/2d/navigation_polygon.cpp
+++ b/scene/2d/navigation_polygon.cpp
@@ -413,6 +413,7 @@ void NavigationPolygonInstance::set_navigation_polygon(const Ref<NavigationPolyg
}
//update_gizmo();
_change_notify("navpoly");
+ update_configuration_warning();
}
@@ -427,6 +428,28 @@ void NavigationPolygonInstance::_navpoly_changed() {
update();
}
+
+String NavigationPolygonInstance::get_configuration_warning() const {
+
+ if (!is_visible() || !is_inside_tree())
+ return String();
+
+ if (!navpoly.is_valid()) {
+ return TTR("A NavigationPolygon resource must be set or created for this node to work. Please set a property or draw a polygon.");
+ }
+ const Node2D *c=this;
+ while(c) {
+
+ if (c->cast_to<Navigation2D>()) {
+ return String();
+ }
+
+ c=c->get_parent()->cast_to<Node2D>();
+ }
+
+ return TTR("NavigationPolygonInstance must be a child or grandchild to a Navigation2D node. It only provides navigation data.");
+}
+
void NavigationPolygonInstance::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_navigation_polygon","navpoly:NavigationPolygon"),&NavigationPolygonInstance::set_navigation_polygon);
diff --git a/scene/2d/navigation_polygon.h b/scene/2d/navigation_polygon.h
index 01307a170b..07fee571f0 100644
--- a/scene/2d/navigation_polygon.h
+++ b/scene/2d/navigation_polygon.h
@@ -77,6 +77,8 @@ public:
void set_navigation_polygon(const Ref<NavigationPolygon>& p_navpoly);
Ref<NavigationPolygon> get_navigation_polygon() const;
+ String get_configuration_warning() const;
+
NavigationPolygonInstance();
};
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 7ef81306b6..134e0153b3 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -148,15 +148,28 @@ void Node2D::set_pos(const Point2& p_pos) {
}
-void Node2D::set_rot(float p_angle) {
+void Node2D::set_rot(float p_radians) {
if (_xform_dirty)
((Node2D*)this)->_update_xform_values();
- angle=p_angle;
+ angle=p_radians;
_update_transform();
_change_notify("transform/rot");
}
+void Node2D::set_rotd(float p_degrees) {
+
+ set_rot(Math::deg2rad(p_degrees));
+}
+
+// Kept for compatibility after rename to set_rotd.
+// Could be removed after a couple releases.
+void Node2D::_set_rotd(float p_degrees) {
+
+ WARN_PRINT("Deprecated method Node2D._set_rotd(): This method was renamed to set_rotd. Please adapt your code accordingly, as the old method will be obsoleted.");
+ set_rotd(p_degrees);
+}
+
void Node2D::set_scale(const Size2& p_scale) {
if (_xform_dirty)
@@ -183,21 +196,22 @@ float Node2D::get_rot() const {
return angle;
}
-Size2 Node2D::get_scale() const {
- if (_xform_dirty)
- ((Node2D*)this)->_update_xform_values();
+float Node2D::get_rotd() const {
- return _scale;
+ return Math::rad2deg(get_rot());
}
+// Kept for compatibility after rename to get_rotd.
+// Could be removed after a couple releases.
+float Node2D::_get_rotd() const {
-void Node2D::_set_rotd(float p_angle) {
-
- set_rot(Math::deg2rad(p_angle));
+ WARN_PRINT("Deprecated method Node2D._get_rotd(): This method was renamed to get_rotd. Please adapt your code accordingly, as the old method will be obsoleted.");
+ return get_rotd();
}
+Size2 Node2D::get_scale() const {
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
-float Node2D::_get_rotd() const {
-
- return Math::rad2deg(get_rot());
+ return _scale;
}
@@ -361,16 +375,18 @@ float Node2D::get_angle_to(const Vector2& p_pos) const {
void Node2D::_bind_methods() {
-
+ // TODO: Obsolete those two methods (old name) properly (GH-4397)
ObjectTypeDB::bind_method(_MD("_get_rotd"),&Node2D::_get_rotd);
- ObjectTypeDB::bind_method(_MD("_set_rotd"),&Node2D::_set_rotd);
+ ObjectTypeDB::bind_method(_MD("_set_rotd","degrees"),&Node2D::_set_rotd);
ObjectTypeDB::bind_method(_MD("set_pos","pos"),&Node2D::set_pos);
- ObjectTypeDB::bind_method(_MD("set_rot","rot"),&Node2D::set_rot);
+ ObjectTypeDB::bind_method(_MD("set_rot","radians"),&Node2D::set_rot);
+ ObjectTypeDB::bind_method(_MD("set_rotd","degrees"),&Node2D::set_rotd);
ObjectTypeDB::bind_method(_MD("set_scale","scale"),&Node2D::set_scale);
ObjectTypeDB::bind_method(_MD("get_pos"),&Node2D::get_pos);
ObjectTypeDB::bind_method(_MD("get_rot"),&Node2D::get_rot);
+ ObjectTypeDB::bind_method(_MD("get_rotd"),&Node2D::get_rotd);
ObjectTypeDB::bind_method(_MD("get_scale"),&Node2D::get_scale);
ObjectTypeDB::bind_method(_MD("rotate","radians"),&Node2D::rotate);
@@ -400,7 +416,7 @@ void Node2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_relative_transform_to_parent","parent"),&Node2D::get_relative_transform_to_parent);
ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2,"transform/pos"),_SCS("set_pos"),_SCS("get_pos"));
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL,"transform/rot",PROPERTY_HINT_RANGE,"-1440,1440,0.1"),_SCS("_set_rotd"),_SCS("_get_rotd"));
+ ADD_PROPERTYNZ(PropertyInfo(Variant::REAL,"transform/rot",PROPERTY_HINT_RANGE,"-1440,1440,0.1"),_SCS("set_rotd"),_SCS("get_rotd"));
ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2,"transform/scale"),_SCS("set_scale"),_SCS("get_scale"));
ADD_PROPERTYNZ(PropertyInfo(Variant::INT,"z/z",PROPERTY_HINT_RANGE,itos(VS::CANVAS_ITEM_Z_MIN)+","+itos(VS::CANVAS_ITEM_Z_MAX)+",1"),_SCS("set_z"),_SCS("get_z"));
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL,"z/relative"),_SCS("set_z_as_relative"),_SCS("is_z_relative"));
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index 49d616fc1f..b0c628fd94 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -47,6 +47,7 @@ class Node2D : public CanvasItem {
void _update_transform();
+ // Deprecated, should be removed in a future version.
void _set_rotd(float p_angle);
float _get_rotd() const;
@@ -69,7 +70,8 @@ public:
virtual bool edit_has_pivot() const;
void set_pos(const Point2& p_pos);
- void set_rot(float p_angle);
+ void set_rot(float p_radians);
+ void set_rotd(float p_degrees);
void set_scale(const Size2& p_scale);
void rotate(float p_radians);
@@ -81,6 +83,7 @@ public:
Point2 get_pos() const;
float get_rot() const;
+ float get_rotd() const;
Size2 get_scale() const;
Point2 get_global_pos() const;
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 7a898e43c9..bf559deb09 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -118,6 +118,16 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2& p_offset,float p_sca
}
+
+String ParallaxLayer::get_configuration_warning() const {
+
+ if (!get_parent() || !get_parent()->cast_to<ParallaxBackground>()) {
+ return TTR("ParallaxLayer node only works when set as child of a ParallaxBackground node.");
+ }
+
+ return String();
+}
+
void ParallaxLayer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_motion_scale","scale"),&ParallaxLayer::set_motion_scale);
diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h
index 6c24a9b9f7..c2d345da47 100644
--- a/scene/2d/parallax_layer.h
+++ b/scene/2d/parallax_layer.h
@@ -56,6 +56,7 @@ public:
void set_base_offset_and_scale(const Point2& p_offsetf,float p_scale);
+ virtual String get_configuration_warning() const;
ParallaxLayer();
};
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index ffea060e82..29dad630d6 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -198,13 +198,21 @@ void ParticleAttractor2D::set_particles_path(NodePath p_path) {
path=p_path;
_update_owner();
+ update_configuration_warning();
}
NodePath ParticleAttractor2D::get_particles_path() const {
return path;
}
+String ParticleAttractor2D::get_configuration_warning() const {
+ if (!has_node(path) || !get_node(path) || !get_node(path)->cast_to<Particles2D>()) {
+ return TTR("Path property must point to a valid Particles2D node to work.");
+ }
+
+ return String();
+}
ParticleAttractor2D::ParticleAttractor2D() {
diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h
index 06dcda7165..b1ae1f5bc1 100644
--- a/scene/2d/particles_2d.h
+++ b/scene/2d/particles_2d.h
@@ -75,6 +75,8 @@ public:
void set_particles_path(NodePath p_path);
NodePath get_particles_path() const;
+ virtual String get_configuration_warning() const;
+
ParticleAttractor2D();
};
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index bd7415aa04..41ca7b1d0f 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -237,6 +237,19 @@ void PathFollow2D::_get_property_list( List<PropertyInfo> *p_list) const{
}
+String PathFollow2D::get_configuration_warning() const {
+
+ if (!is_visible() || !is_inside_tree())
+ return String();
+
+ if (!get_parent() || !get_parent()->cast_to<Path2D>()) {
+ return TTR("PathFollow2D only works when set as a child of a Path2D node.");
+ }
+
+ return String();
+
+}
+
void PathFollow2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_offset","offset"),&PathFollow2D::set_offset);
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 486a8ac9ac..84725e7123 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -109,6 +109,8 @@ public:
void set_cubic_interpolation(bool p_enable);
bool get_cubic_interpolation() const;
+ String get_configuration_warning() const;
+
PathFollow2D();
};
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index cc2e5c0d72..8f0474b765 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -544,7 +544,10 @@ void RigidBody2D::_direct_state_changed(Object *p_state) {
set_global_transform(state->get_transform());
linear_velocity=state->get_linear_velocity();
angular_velocity=state->get_angular_velocity();
- sleeping=state->is_sleeping();
+ if(sleeping!=state->is_sleeping()) {
+ sleeping=state->is_sleeping();
+ emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed);
+ }
if (get_script_instance())
get_script_instance()->call("_integrate_forces",state);
set_block_transform_notify(false); // want it back
@@ -599,6 +602,17 @@ real_t RigidBody2D::get_mass() const{
return mass;
}
+void RigidBody2D::set_inertia(real_t p_inertia) {
+
+ ERR_FAIL_COND(p_inertia<=0);
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_INERTIA,p_inertia);
+}
+
+real_t RigidBody2D::get_inertia() const{
+
+ return Physics2DServer::get_singleton()->body_get_param(get_rid(),Physics2DServer::BODY_PARAM_INERTIA);
+}
+
void RigidBody2D::set_weight(real_t p_weight){
set_mass(p_weight/9.8);
@@ -764,9 +778,9 @@ int RigidBody2D::get_max_contacts_reported() const{
return max_contacts_reported;
}
-void RigidBody2D::apply_impulse(const Vector2& p_pos, const Vector2& p_impulse) {
+void RigidBody2D::apply_impulse(const Vector2& p_offset, const Vector2& p_impulse) {
- Physics2DServer::get_singleton()->body_apply_impulse(get_rid(),p_pos,p_impulse);
+ Physics2DServer::get_singleton()->body_apply_impulse(get_rid(),p_offset,p_impulse);
}
void RigidBody2D::set_applied_force(const Vector2& p_force) {
@@ -779,6 +793,20 @@ Vector2 RigidBody2D::get_applied_force() const {
return Physics2DServer::get_singleton()->body_get_applied_force(get_rid());
};
+void RigidBody2D::set_applied_torque(const float p_torque) {
+
+ Physics2DServer::get_singleton()->body_set_applied_torque(get_rid(), p_torque);
+};
+
+float RigidBody2D::get_applied_torque() const {
+
+ return Physics2DServer::get_singleton()->body_get_applied_torque(get_rid());
+};
+
+void RigidBody2D::add_force(const Vector2& p_offset, const Vector2& p_force) {
+
+ Physics2DServer::get_singleton()->body_add_force(get_rid(),p_offset,p_force);
+}
void RigidBody2D::set_continuous_collision_detection_mode(CCDMode p_mode) {
@@ -855,6 +883,9 @@ void RigidBody2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_mass","mass"),&RigidBody2D::set_mass);
ObjectTypeDB::bind_method(_MD("get_mass"),&RigidBody2D::get_mass);
+ ObjectTypeDB::bind_method(_MD("get_inertia"),&RigidBody2D::get_inertia);
+ ObjectTypeDB::bind_method(_MD("set_inertia","inertia"),&RigidBody2D::set_inertia);
+
ObjectTypeDB::bind_method(_MD("set_weight","weight"),&RigidBody2D::set_weight);
ObjectTypeDB::bind_method(_MD("get_weight"),&RigidBody2D::get_weight);
@@ -892,11 +923,16 @@ void RigidBody2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_continuous_collision_detection_mode"),&RigidBody2D::get_continuous_collision_detection_mode);
ObjectTypeDB::bind_method(_MD("set_axis_velocity","axis_velocity"),&RigidBody2D::set_axis_velocity);
- ObjectTypeDB::bind_method(_MD("apply_impulse","pos","impulse"),&RigidBody2D::apply_impulse);
+ ObjectTypeDB::bind_method(_MD("apply_impulse","offset","impulse"),&RigidBody2D::apply_impulse);
ObjectTypeDB::bind_method(_MD("set_applied_force","force"),&RigidBody2D::set_applied_force);
ObjectTypeDB::bind_method(_MD("get_applied_force"),&RigidBody2D::get_applied_force);
+ ObjectTypeDB::bind_method(_MD("set_applied_torque","torque"),&RigidBody2D::set_applied_torque);
+ ObjectTypeDB::bind_method(_MD("get_applied_torque"),&RigidBody2D::get_applied_torque);
+
+ ObjectTypeDB::bind_method(_MD("add_force","offset","force"),&RigidBody2D::add_force);
+
ObjectTypeDB::bind_method(_MD("set_sleeping","sleeping"),&RigidBody2D::set_sleeping);
ObjectTypeDB::bind_method(_MD("is_sleeping"),&RigidBody2D::is_sleeping);
@@ -934,6 +970,7 @@ void RigidBody2D::_bind_methods() {
ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape")));
ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body")));
ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body")));
+ ADD_SIGNAL( MethodInfo("sleeping_state_changed"));
BIND_CONSTANT( MODE_STATIC );
BIND_CONSTANT( MODE_KINEMATIC );
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 999e63dd5d..5af65bff33 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -217,6 +217,9 @@ public:
void set_mass(real_t p_mass);
real_t get_mass() const;
+ void set_inertia(real_t p_inertia);
+ real_t get_inertia() const;
+
void set_weight(real_t p_weight);
real_t get_weight() const;
@@ -261,11 +264,16 @@ public:
void set_continuous_collision_detection_mode(CCDMode p_mode);
CCDMode get_continuous_collision_detection_mode() const;
- void apply_impulse(const Vector2& p_pos, const Vector2& p_impulse);
+ void apply_impulse(const Vector2& p_offset, const Vector2& p_impulse);
void set_applied_force(const Vector2& p_force);
Vector2 get_applied_force() const;
+ void set_applied_torque(const float p_torque);
+ float get_applied_torque() const;
+
+ void add_force(const Vector2& p_offset, const Vector2& p_force);
+
Array get_colliding_bodies() const; //function for script
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index fc6986327f..03ced12c55 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -145,7 +145,18 @@ void Polygon2D::_notification(int p_what) {
Vector<Color> colors;
- colors.push_back(color);
+ int color_len=vertex_colors.size();
+ colors.resize(len);
+ {
+ DVector<Color>::Read color_r=vertex_colors.read();
+ for(int i=0;i<color_len && i<len;i++){
+ colors[i]=color_r[i];
+ }
+ for(int i=color_len;i<len;i++){
+ colors[i]=color;
+ }
+ }
+
Vector<int> indices = Geometry::triangulate_polygon(points);
VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(),indices,points,colors,uvs,texture.is_valid()?texture->get_rid():RID());
@@ -188,6 +199,16 @@ Color Polygon2D::get_color() const{
return color;
}
+void Polygon2D::set_vertex_colors(const DVector<Color>& p_colors){
+
+ vertex_colors=p_colors;
+ update();
+}
+DVector<Color> Polygon2D::get_vertex_colors() const{
+
+ return vertex_colors;
+}
+
void Polygon2D::set_texture(const Ref<Texture>& p_texture){
texture=p_texture;
@@ -293,6 +314,9 @@ void Polygon2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_color","color"),&Polygon2D::set_color);
ObjectTypeDB::bind_method(_MD("get_color"),&Polygon2D::get_color);
+ ObjectTypeDB::bind_method(_MD("set_vertex_colors","vertex_colors"),&Polygon2D::set_vertex_colors);
+ ObjectTypeDB::bind_method(_MD("get_vertex_colors"),&Polygon2D::get_vertex_colors);
+
ObjectTypeDB::bind_method(_MD("set_texture","texture"),&Polygon2D::set_texture);
ObjectTypeDB::bind_method(_MD("get_texture"),&Polygon2D::get_texture);
@@ -323,6 +347,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"polygon"),_SCS("set_polygon"),_SCS("get_polygon"));
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"uv"),_SCS("set_uv"),_SCS("get_uv"));
ADD_PROPERTY( PropertyInfo(Variant::COLOR,"color"),_SCS("set_color"),_SCS("get_color"));
+ ADD_PROPERTY( PropertyInfo(Variant::COLOR_ARRAY,"vertex_colors"),_SCS("set_vertex_colors"),_SCS("get_vertex_colors"));
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_offset"),_SCS("get_offset"));
ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture/texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture"));
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"texture/offset"),_SCS("set_texture_offset"),_SCS("get_texture_offset"));
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index 517b623ccd..eaa642787c 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -9,6 +9,7 @@ class Polygon2D : public Node2D {
DVector<Vector2> polygon;
DVector<Vector2> uv;
+ DVector<Color> vertex_colors;
Color color;
Ref<Texture> texture;
Vector2 tex_scale;
@@ -40,6 +41,9 @@ public:
void set_color(const Color& p_color);
Color get_color() const;
+ void set_vertex_colors(const DVector<Color>& p_colors);
+ DVector<Color> get_vertex_colors() const;
+
void set_texture(const Ref<Texture>& p_texture);
Ref<Texture> get_texture() const;
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 4a774b0198..6cda52fa4e 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -33,7 +33,7 @@
void RayCast2D::set_cast_to(const Vector2& p_point) {
cast_to=p_point;
- if (is_inside_tree() && get_tree()->is_editor_hint())
+ if (is_inside_tree() && (get_tree()->is_editor_hint() || get_tree()->is_debugging_collisions_hint()))
update();
}
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index 6dcd980822..4de648a1db 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -97,6 +97,8 @@ void RemoteTransform2D::set_remote_node(const NodePath& p_remote_node) {
remote_node=p_remote_node;
if (is_inside_tree())
_update_cache();
+
+ update_configuration_warning();
}
NodePath RemoteTransform2D::get_remote_node() const{
@@ -105,6 +107,15 @@ NodePath RemoteTransform2D::get_remote_node() const{
}
+String RemoteTransform2D::get_configuration_warning() const {
+
+ if (!has_node(remote_node) || !get_node(remote_node) || !get_node(remote_node)->cast_to<Node2D>()) {
+ return TTR("Path property must point to a valid Node2D node to work.");
+ }
+
+ return String();
+}
+
void RemoteTransform2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_remote_node","path"),&RemoteTransform2D::set_remote_node);
diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h
index 4a5f5f72ea..0ea1438f0a 100644
--- a/scene/2d/remote_transform_2d.h
+++ b/scene/2d/remote_transform_2d.h
@@ -48,5 +48,7 @@ public:
void set_remote_node(const NodePath& p_remote_node);
NodePath get_remote_node() const;
+ virtual String get_configuration_warning() const;
+
RemoteTransform2D();
};
diff --git a/scene/2d/sample_player_2d.cpp b/scene/2d/sample_player_2d.cpp
index bf09130238..4d719b532b 100644
--- a/scene/2d/sample_player_2d.cpp
+++ b/scene/2d/sample_player_2d.cpp
@@ -103,6 +103,7 @@ void SamplePlayer2D::set_sample_library(const Ref<SampleLibrary>& p_library) {
library=p_library;
_change_notify();
+ update_configuration_warning();
}
Ref<SampleLibrary> SamplePlayer2D::get_sample_library() const {
@@ -207,6 +208,14 @@ float SamplePlayer2D::get_random_pitch_scale() const {
return random_pitch_scale;
}
+String SamplePlayer2D::get_configuration_warning() const {
+
+ if (library.is_null()) {
+ return TTR("A SampleLibrary resource must be created or set in the 'samples' property in order for SamplePlayer to play sound.");
+ }
+
+ return String();
+}
void SamplePlayer2D::_bind_methods() {
diff --git a/scene/2d/sample_player_2d.h b/scene/2d/sample_player_2d.h
index eddf84f77b..5ab7f024d3 100644
--- a/scene/2d/sample_player_2d.h
+++ b/scene/2d/sample_player_2d.h
@@ -83,6 +83,8 @@ public:
void set_random_pitch_scale(float p_scale);
float get_random_pitch_scale() const;
+ String get_configuration_warning() const;
+
SamplePlayer2D();
~SamplePlayer2D();
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index b2902b2867..3e6384ea2c 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -529,6 +529,25 @@ Rect2 ViewportSprite::get_item_rect() const {
return Rect2(ofs,s);
}
+String ViewportSprite::get_configuration_warning() const {
+
+ if (!has_node(viewport_path) || !get_node(viewport_path) || !get_node(viewport_path)->cast_to<Viewport>()) {
+ return TTR("Path property must point to a valid Viewport node to work. Such Viewport must be set to 'render target' mode.");
+ } else {
+
+ Node *n = get_node(viewport_path);
+ if (n) {
+ Viewport *vp = n->cast_to<Viewport>();
+ if (!vp->is_set_as_render_target()) {
+
+ return TTR("The Viewport set in the path property must be set as 'render target' in order for this sprite to work.");
+ }
+ }
+ }
+
+ return String();
+
+}
void ViewportSprite::_bind_methods() {
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index cbcaec9aeb..f789538b1d 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -142,6 +142,8 @@ public:
virtual Rect2 get_item_rect() const;
+ virtual String get_configuration_warning() const;
+
ViewportSprite();
};
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 34a67c088f..1f16b36466 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -504,6 +504,7 @@ void TileMap::_update_dirty_quadrants() {
}
dirty_quadrant_list.remove( dirty_quadrant_list.first() );
+ quadrant_order_dirty=true;
}
@@ -524,6 +525,14 @@ void TileMap::_update_dirty_quadrants() {
quadrant_order_dirty=false;
}
+ for(int i=0;i<get_child_count();i++) {
+
+ CanvasItem *c=get_child(i)->cast_to<CanvasItem>();
+
+ if (c)
+ VS::get_singleton()->canvas_item_raise(c->get_canvas_item());
+ }
+
_recompute_rect_cache();
}
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 60fa7f69c8..12524a2192 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -30,6 +30,7 @@
#include "scene/scene_string_names.h"
#include "scene/2d/physics_body_2d.h"
+#include "scene/2d/animated_sprite.h"
#include "scene/animation/animation_player.h"
#include "scene/scene_string_names.h"
#include "particles_2d.h"
@@ -204,6 +205,16 @@ void VisibilityEnabler2D::_find_nodes(Node* p_node) {
}
+ if (enabler[ENABLER_PAUSE_ANIMATED_SPRITES]) {
+
+ AnimatedSprite *as = p_node->cast_to<AnimatedSprite>();
+ if (as) {
+ add=true;
+ }
+
+ }
+
+
if (enabler[ENABLER_PAUSE_PARTICLES]) {
Particles2D *ps = p_node->cast_to<Particles2D>();
@@ -301,6 +312,17 @@ void VisibilityEnabler2D::_change_node_state(Node* p_node,bool p_enabled) {
ap->set_active(p_enabled);
}
}
+ {
+ AnimatedSprite *as=p_node->cast_to<AnimatedSprite>();
+
+ if (as) {
+
+ if (p_enabled)
+ as->play();
+ else
+ as->stop();
+ }
+ }
{
Particles2D *ps=p_node->cast_to<Particles2D>();
@@ -324,6 +346,16 @@ void VisibilityEnabler2D::_node_removed(Node* p_node) {
}
+String VisibilityEnabler2D::get_configuration_warning() const {
+#ifdef TOOLS_ENABLED
+ if (is_inside_tree() && get_parent() && (get_parent()->get_filename()==String() && get_parent()!=get_tree()->get_edited_scene_root())) {
+ return TTR("VisibilityEnable2D works best when used with the edited scene root directly as parent.");
+ }
+#endif
+ return String();
+}
+
+
void VisibilityEnabler2D::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_enabler","enabler","enabled"),&VisibilityEnabler2D::set_enabler);
@@ -333,12 +365,14 @@ void VisibilityEnabler2D::_bind_methods(){
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/pause_animations"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATIONS );
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/freeze_bodies"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_FREEZE_BODIES);
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/pause_particles"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PAUSE_PARTICLES);
+ ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/pause_animated_sprites"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATED_SPRITES);
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/process_parent"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PARENT_PROCESS);
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/fixed_process_parent"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PARENT_FIXED_PROCESS);
BIND_CONSTANT( ENABLER_FREEZE_BODIES );
BIND_CONSTANT( ENABLER_PAUSE_ANIMATIONS );
BIND_CONSTANT( ENABLER_PAUSE_PARTICLES );
+ BIND_CONSTANT( ENABLER_PAUSE_ANIMATED_SPRITES );
BIND_CONSTANT( ENABLER_PARENT_PROCESS );
BIND_CONSTANT( ENABLER_PARENT_FIXED_PROCESS );
BIND_CONSTANT( ENABLER_MAX);
diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h
index 6ec24fd4d0..354ccf4345 100644
--- a/scene/2d/visibility_notifier_2d.h
+++ b/scene/2d/visibility_notifier_2d.h
@@ -76,6 +76,7 @@ public:
ENABLER_PAUSE_PARTICLES,
ENABLER_PARENT_PROCESS,
ENABLER_PARENT_FIXED_PROCESS,
+ ENABLER_PAUSE_ANIMATED_SPRITES,
ENABLER_MAX
};
@@ -102,6 +103,8 @@ public:
void set_enabler(Enabler p_enabler,bool p_enable);
bool is_enabler_enabled(Enabler p_enabler) const;
+ String get_configuration_warning() const;
+
VisibilityEnabler2D();
};
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index 7d4235e051..a8a4122016 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -519,6 +519,60 @@ bool Area::overlaps_body(Node* p_body) const{
return E->get().in_tree;
}
+void Area::set_collision_mask(uint32_t p_mask) {
+
+ collision_mask=p_mask;
+ PhysicsServer::get_singleton()->area_set_collision_mask(get_rid(),p_mask);
+}
+
+uint32_t Area::get_collision_mask() const {
+
+ return collision_mask;
+}
+void Area::set_layer_mask(uint32_t p_mask) {
+
+ layer_mask=p_mask;
+ PhysicsServer::get_singleton()->area_set_layer_mask(get_rid(),p_mask);
+}
+
+uint32_t Area::get_layer_mask() const {
+
+ return layer_mask;
+}
+
+void Area::set_collision_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask|=1<<p_bit;
+ else
+ mask&=~(1<<p_bit);
+ set_collision_mask(mask);
+
+}
+
+bool Area::get_collision_mask_bit(int p_bit) const{
+
+ return get_collision_mask()&(1<<p_bit);
+}
+
+
+void Area::set_layer_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_layer_mask();
+ if (p_value)
+ mask|=1<<p_bit;
+ else
+ mask&=~(1<<p_bit);
+ set_layer_mask(mask);
+
+}
+
+bool Area::get_layer_mask_bit(int p_bit) const{
+
+ return get_layer_mask()&(1<<p_bit);
+}
+
void Area::_bind_methods() {
@@ -552,6 +606,18 @@ void Area::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_priority","priority"),&Area::set_priority);
ObjectTypeDB::bind_method(_MD("get_priority"),&Area::get_priority);
+ ObjectTypeDB::bind_method(_MD("set_collision_mask","collision_mask"),&Area::set_collision_mask);
+ ObjectTypeDB::bind_method(_MD("get_collision_mask"),&Area::get_collision_mask);
+
+ ObjectTypeDB::bind_method(_MD("set_layer_mask","layer_mask"),&Area::set_layer_mask);
+ ObjectTypeDB::bind_method(_MD("get_layer_mask"),&Area::get_layer_mask);
+
+ ObjectTypeDB::bind_method(_MD("set_collision_mask_bit","bit","value"),&Area::set_collision_mask_bit);
+ ObjectTypeDB::bind_method(_MD("get_collision_mask_bit","bit"),&Area::get_collision_mask_bit);
+
+ ObjectTypeDB::bind_method(_MD("set_layer_mask_bit","bit","value"),&Area::set_layer_mask_bit);
+ ObjectTypeDB::bind_method(_MD("get_layer_mask_bit","bit"),&Area::get_layer_mask_bit);
+
ObjectTypeDB::bind_method(_MD("set_monitorable","enable"),&Area::set_monitorable);
ObjectTypeDB::bind_method(_MD("is_monitorable"),&Area::is_monitorable);
@@ -562,8 +628,8 @@ void Area::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_overlapping_bodies"),&Area::get_overlapping_bodies);
ObjectTypeDB::bind_method(_MD("get_overlapping_areas"),&Area::get_overlapping_areas);
- ObjectTypeDB::bind_method(_MD("overlaps_body:PhysicsBody","body"),&Area::overlaps_body);
- ObjectTypeDB::bind_method(_MD("overlaps_area:Area","area"),&Area::overlaps_area);
+ ObjectTypeDB::bind_method(_MD("overlaps_body","body"),&Area::overlaps_body);
+ ObjectTypeDB::bind_method(_MD("overlaps_area","area"),&Area::overlaps_area);
ObjectTypeDB::bind_method(_MD("_body_inout"),&Area::_body_inout);
ObjectTypeDB::bind_method(_MD("_area_inout"),&Area::_area_inout);
@@ -589,6 +655,8 @@ void Area::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::INT,"priority",PROPERTY_HINT_RANGE,"0,128,1"),_SCS("set_priority"),_SCS("get_priority"));
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitoring"),_SCS("set_enable_monitoring"),_SCS("is_monitoring_enabled"));
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitorable"),_SCS("set_monitorable"),_SCS("is_monitorable"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"collision/layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"collision/mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_collision_mask"),_SCS("get_collision_mask"));
}
@@ -604,6 +672,8 @@ Area::Area() : CollisionObject(PhysicsServer::get_singleton()->area_create(),tru
angular_damp=1;
priority=0;
monitoring=false;
+ collision_mask=1;
+ layer_mask=1;
set_ray_pickable(false);
set_enable_monitoring(true);
set_monitorable(true);
diff --git a/scene/3d/area.h b/scene/3d/area.h
index c250d27fb1..440a7d2030 100644
--- a/scene/3d/area.h
+++ b/scene/3d/area.h
@@ -54,6 +54,8 @@ private:
real_t gravity_distance_scale;
real_t angular_damp;
real_t linear_damp;
+ uint32_t collision_mask;
+ uint32_t layer_mask;
int priority;
bool monitoring;
bool monitorable;
@@ -157,6 +159,18 @@ public:
void set_monitorable(bool p_enable);
bool is_monitorable() const;
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_layer_mask(uint32_t p_mask);
+ uint32_t get_layer_mask() const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
+ void set_layer_mask_bit(int p_bit, bool p_value);
+ bool get_layer_mask_bit(int p_bit) const;
+
Array get_overlapping_bodies() const;
Array get_overlapping_areas() const; //function for script
diff --git a/scene/3d/body_shape.cpp b/scene/3d/body_shape.cpp
index 3a47371de3..e62ab394af 100644
--- a/scene/3d/body_shape.cpp
+++ b/scene/3d/body_shape.cpp
@@ -398,6 +398,19 @@ int CollisionShape::_get_update_shape_index() const{
return update_shape_index;
}
+String CollisionShape::get_configuration_warning() const {
+
+ if (!get_parent()->cast_to<CollisionObject>()) {
+ return TTR("CollisionShape only serves to provide a collision shape to a CollisionObject derived node. Please only use it as a child of Area, StaticBody, RigidBody, KinematicBody, etc. to give them a shape.");
+ }
+
+ if (!shape.is_valid()) {
+ return TTR("A shape must be provided for CollisionShape to function. Please create a shape resource for it!");
+ }
+
+ return String();
+}
+
void CollisionShape::_bind_methods() {
diff --git a/scene/3d/body_shape.h b/scene/3d/body_shape.h
index dd005c0edd..a3289bf26a 100644
--- a/scene/3d/body_shape.h
+++ b/scene/3d/body_shape.h
@@ -90,6 +90,9 @@ public:
int get_collision_object_shape_index() const { return _get_update_shape_index(); }
+
+ String get_configuration_warning() const;
+
CollisionShape();
~CollisionShape();
};
diff --git a/scene/3d/bone_attachment.cpp b/scene/3d/bone_attachment.cpp
index 1628ccc15e..56b61d40e2 100644
--- a/scene/3d/bone_attachment.cpp
+++ b/scene/3d/bone_attachment.cpp
@@ -80,6 +80,7 @@ void BoneAttachment::_check_bind() {
int idx = sk->find_bone(bone_name);
if (idx!=-1) {
sk->bind_child_node_to_bone(idx,this);;
+ set_transform(sk->get_bone_global_pose(idx));
bound=true;
}
}
diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp
index 3b14e1d767..e05f29714b 100644
--- a/scene/3d/collision_polygon.cpp
+++ b/scene/3d/collision_polygon.cpp
@@ -231,6 +231,19 @@ float CollisionPolygon::get_depth() const {
return depth;
}
+String CollisionPolygon::get_configuration_warning() const {
+
+ if (!get_parent()->cast_to<CollisionObject>()) {
+ return TTR("CollisionPolygon only serves to provide a collision shape to a CollisionObject derived node. Please only use it as a child of Area, StaticBody, RigidBody, KinematicBody, etc. to give them a shape.");
+ }
+
+ if (polygon.empty()) {
+ return TTR("An empty CollisionPolygon has no effect on collision.");
+
+ }
+
+ return String();
+}
void CollisionPolygon::_bind_methods() {
diff --git a/scene/3d/collision_polygon.h b/scene/3d/collision_polygon.h
index 9b9afea34f..3d190a02b3 100644
--- a/scene/3d/collision_polygon.h
+++ b/scene/3d/collision_polygon.h
@@ -55,6 +55,8 @@ public:
int get_collision_object_first_shape() const { return shape_from; }
int get_collision_object_last_shape() const { return shape_to; }
+ String get_configuration_warning() const;
+
CollisionPolygon();
};
diff --git a/scene/3d/listener.cpp b/scene/3d/listener.cpp
new file mode 100644
index 0000000000..bf42a5c92e
--- /dev/null
+++ b/scene/3d/listener.cpp
@@ -0,0 +1,167 @@
+#include "listener.h"
+
+#include "scene/resources/mesh.h"
+
+void Listener::_update_audio_listener_state() {
+
+
+}
+
+void Listener::_request_listener_update() {
+
+ _update_listener();
+}
+
+bool Listener::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (p_name == "current") {
+ if (p_value.operator bool()) {
+ make_current();
+ }
+ else {
+ clear_current();
+ }
+ }
+ else
+ return false;
+
+ return true;
+}
+bool Listener::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (p_name == "current") {
+ if (is_inside_tree() && get_tree()->is_node_being_edited(this)) {
+ r_ret = current;
+ }
+ else {
+ r_ret = is_current();
+ }
+ }
+ else
+ return false;
+
+ return true;
+}
+
+void Listener::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo( Variant::BOOL, "current" ) );
+}
+
+void Listener::_update_listener() {
+
+ if (is_inside_tree() && is_current()) {
+ get_viewport()->_listener_transform_changed_notify();
+
+ }
+}
+
+void Listener::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+ bool first_listener = get_viewport()->_listener_add(this);
+ if (!get_tree()->is_node_being_edited(this) && (current || first_listener))
+ make_current();
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ _request_listener_update();
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ if (!get_tree()->is_node_being_edited(this)) {
+ if (is_current()) {
+ clear_current();
+ current=true; //keep it true
+
+ } else {
+ current=false;
+ }
+ }
+
+ get_viewport()->_listener_remove(this);
+
+
+ } break;
+
+
+ }
+
+}
+
+
+Transform Listener::get_listener_transform() const {
+
+ return get_global_transform().orthonormalized();
+}
+
+void Listener::make_current() {
+
+ current=true;
+
+ if (!is_inside_tree())
+ return;
+
+ get_viewport()->_listener_set(this);
+}
+
+
+
+
+void Listener::clear_current() {
+
+ current=false;
+ if (!is_inside_tree())
+ return;
+
+ if (get_viewport()->get_listener()==this) {
+ get_viewport()->_listener_set(NULL);
+ get_viewport()->_listener_make_next_current(this);
+ }
+
+}
+
+bool Listener::is_current() const {
+
+ if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) {
+
+ return get_viewport()->get_listener()==this;
+ } else
+ return current;
+
+ return false;
+}
+
+bool Listener::_can_gizmo_scale() const {
+
+ return false;
+}
+
+RES Listener::_get_gizmo_geometry() const {
+ Ref<Mesh> mesh = memnew(Mesh);
+
+ return mesh;
+}
+
+void Listener::_bind_methods() {
+
+ ObjectTypeDB::bind_method( _MD("make_current"),&Listener::make_current );
+ ObjectTypeDB::bind_method( _MD("clear_current"),&Listener::clear_current );
+ ObjectTypeDB::bind_method( _MD("is_current"),&Listener::is_current );
+ ObjectTypeDB::bind_method( _MD("get_listener_transform"),&Listener::get_listener_transform );
+}
+
+Listener::Listener() {
+
+ current=false;
+ force_change=false;
+ //active=false;
+}
+
+
+Listener::~Listener() {
+
+}
+
+
diff --git a/scene/3d/listener.h b/scene/3d/listener.h
new file mode 100644
index 0000000000..bf0281a8e0
--- /dev/null
+++ b/scene/3d/listener.h
@@ -0,0 +1,53 @@
+#ifndef LISTENER_H
+#define LISTENER_H
+
+
+#include "scene/3d/spatial.h"
+#include "scene/main/viewport.h"
+
+class Listener : public Spatial {
+
+ OBJ_TYPE(Listener, Spatial);
+private:
+
+ bool force_change;
+ bool current;
+
+ RID scenario_id;
+
+ virtual bool _can_gizmo_scale() const;
+ virtual RES _get_gizmo_geometry() const;
+
+friend class Viewport;
+ void _update_audio_listener_state();
+protected:
+
+ void _update_listener();
+ virtual void _request_listener_update();
+
+ 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;
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+
+ void make_current();
+ void clear_current();
+ bool is_current() const;
+
+ virtual Transform get_listener_transform() const;
+
+ void set_visible_layers(uint32_t p_layers);
+ uint32_t get_visible_layers() const;
+
+ Vector<Plane> get_frustum() const;
+
+ Listener();
+ ~Listener();
+
+};
+
+#endif
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index cfe273fa20..ef956e8ad9 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -31,8 +31,8 @@
#include "skeleton.h"
#include "physics_body.h"
#include "body_shape.h"
-
-
+#include "scene/scene_string_names.h"
+#include "core_string_names.h"
bool MeshInstance::_set(const StringName& p_name, const Variant& p_value) {
//this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else.
@@ -43,13 +43,22 @@ bool MeshInstance::_set(const StringName& p_name, const Variant& p_value) {
Map<StringName,MorphTrack>::Element *E = morph_tracks.find(p_name);
- if (!E)
- return false;
+ if (E) {
+ E->get().value=p_value;
+ VisualServer::get_singleton()->instance_set_morph_target_weight(get_instance(),E->get().idx,E->get().value);
+ return true;
+ }
- E->get().value=p_value;
- VisualServer::get_singleton()->instance_set_morph_target_weight(get_instance(),E->get().idx,E->get().value);
+ if (p_name.operator String().begins_with("material/")) {
+ int idx = p_name.operator String().get_slicec('/',1).to_int();
+ if (idx>=materials.size() || idx<0)
+ return false;
- return true;
+ set_surface_material(idx,p_value);
+ return true;
+ }
+
+ return false;
}
bool MeshInstance::_get(const StringName& p_name,Variant &r_ret) const {
@@ -59,12 +68,19 @@ bool MeshInstance::_get(const StringName& p_name,Variant &r_ret) const {
return false;
const Map<StringName,MorphTrack>::Element *E = morph_tracks.find(p_name);
- if (!E)
- return false;
-
- r_ret = E->get().value;
+ if (E) {
+ r_ret = E->get().value;
+ return true;
+ }
- return true;
+ if (p_name.operator String().begins_with("material/")) {
+ int idx = p_name.operator String().get_slicec('/',1).to_int();
+ if (idx>=materials.size() || idx<0)
+ return false;
+ r_ret=materials[idx];
+ return true;
+ }
+ return false;
}
void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const {
@@ -80,6 +96,12 @@ void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const {
for(List<String>::Element *E=ls.front();E;E=E->next()) {
p_list->push_back( PropertyInfo(Variant::REAL,E->get(),PROPERTY_HINT_RANGE,"0,1,0.01"));
}
+
+ if (mesh.is_valid()) {
+ for(int i=0;i<mesh->get_surface_count();i++) {
+ p_list->push_back( PropertyInfo(Variant::OBJECT, "material/"+itos(i), PROPERTY_HINT_RESOURCE_TYPE, "Material"));
+ }
+ }
}
@@ -87,6 +109,14 @@ void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const {
void MeshInstance::set_mesh(const Ref<Mesh>& p_mesh) {
+ if (mesh==p_mesh)
+ return;
+
+ if (mesh.is_valid()) {
+ mesh->disconnect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->_mesh_changed);
+ materials.clear();
+ }
+
mesh=p_mesh;
morph_tracks.clear();
@@ -100,13 +130,17 @@ void MeshInstance::set_mesh(const Ref<Mesh>& p_mesh) {
mt.value=0;
morph_tracks["morph/"+String(mesh->get_morph_target_name(i))]=mt;
}
+
+ mesh->connect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->_mesh_changed);
+ materials.resize(mesh->get_surface_count());
+
set_base(mesh->get_rid());
} else {
set_base(RID());
}
- _change_notify("mesh");
+ _change_notify();
}
Ref<Mesh> MeshInstance::get_mesh() const {
@@ -232,6 +266,32 @@ void MeshInstance::_notification(int p_what) {
}
+void MeshInstance::set_surface_material(int p_surface,const Ref<Material>& p_material) {
+
+ ERR_FAIL_INDEX(p_surface,materials.size());
+
+ materials[p_surface]=p_material;
+
+ if (materials[p_surface].is_valid())
+ VS::get_singleton()->instance_set_surface_material(get_instance(),p_surface,materials[p_surface]->get_rid());
+ else
+ VS::get_singleton()->instance_set_surface_material(get_instance(),p_surface,RID());
+
+}
+
+Ref<Material> MeshInstance::get_surface_material(int p_surface) const {
+
+ ERR_FAIL_INDEX_V(p_surface,materials.size(),Ref<Material>());
+
+ return materials[p_surface];
+}
+
+
+void MeshInstance::_mesh_changed() {
+
+ materials.resize( mesh->get_surface_count() );
+}
+
void MeshInstance::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_mesh","mesh:Mesh"),&MeshInstance::set_mesh);
@@ -243,6 +303,7 @@ void MeshInstance::_bind_methods() {
ObjectTypeDB::set_method_flags("MeshInstance","create_trimesh_collision",METHOD_FLAGS_DEFAULT);
ObjectTypeDB::bind_method(_MD("create_convex_collision"),&MeshInstance::create_convex_collision);
ObjectTypeDB::set_method_flags("MeshInstance","create_convex_collision",METHOD_FLAGS_DEFAULT);
+ ObjectTypeDB::bind_method(_MD("_mesh_changed"),&MeshInstance::_mesh_changed);
ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "mesh/mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh" ), _SCS("set_mesh"), _SCS("get_mesh"));
ADD_PROPERTY( PropertyInfo (Variant::NODE_PATH, "mesh/skeleton"), _SCS("set_skeleton_path"), _SCS("get_skeleton_path"));
}
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 7c605c2d6d..fd8faf38b4 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -50,7 +50,9 @@ class MeshInstance : public GeometryInstance {
};
Map<StringName,MorphTrack> morph_tracks;
+ Vector<Ref<Material> > materials;
+ void _mesh_changed();
void _resolve_skeleton_path();
protected:
@@ -69,6 +71,9 @@ public:
void set_skeleton_path(const NodePath& p_skeleton);
NodePath get_skeleton_path();
+ void set_surface_material(int p_surface,const Ref<Material>& p_material);
+ Ref<Material> get_surface_material(int p_surface) const;
+
Node* create_trimesh_collision_node();
void create_trimesh_collision();
diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp
index a238a8ff22..3adf282f13 100644
--- a/scene/3d/navigation_mesh.cpp
+++ b/scene/3d/navigation_mesh.cpp
@@ -329,6 +329,7 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh>& p_na
nav_id = navigation->navmesh_create(navmesh,get_relative_transform(navigation),this);
}
update_gizmo();
+ update_configuration_warning();
}
@@ -337,6 +338,27 @@ Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const{
return navmesh;
}
+String NavigationMeshInstance::get_configuration_warning() const {
+
+ if (!is_visible() || !is_inside_tree())
+ return String();
+
+ if (!navmesh.is_valid()) {
+ return TTR("A NavigationMesh resource must be set or created for this node to work.");
+ }
+ const Spatial *c=this;
+ while(c) {
+
+ if (c->cast_to<Navigation>())
+ return String();
+
+ c=c->get_parent()->cast_to<Spatial>();
+ }
+
+ return TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data.");
+}
+
+
void NavigationMeshInstance::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_navigation_mesh","navmesh"),&NavigationMeshInstance::set_navigation_mesh);
diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h
index 1e53b2127a..cb3b5d95f6 100644
--- a/scene/3d/navigation_mesh.h
+++ b/scene/3d/navigation_mesh.h
@@ -78,6 +78,8 @@ public:
void set_navigation_mesh(const Ref<NavigationMesh>& p_navmesh);
Ref<NavigationMesh> get_navigation_mesh() const;
+ String get_configuration_warning() const;
+
NavigationMeshInstance();
};
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 1a2665b6ad..243cb31aca 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -69,6 +69,50 @@ uint32_t PhysicsBody::get_layer_mask() const {
return layer_mask;
}
+void PhysicsBody::set_collision_mask(uint32_t p_mask) {
+
+ collision_mask=p_mask;
+ PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(),p_mask);
+}
+
+uint32_t PhysicsBody::get_collision_mask() const {
+
+ return collision_mask;
+}
+
+void PhysicsBody::set_collision_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask|=1<<p_bit;
+ else
+ mask&=~(1<<p_bit);
+ set_collision_mask(mask);
+
+}
+
+bool PhysicsBody::get_collision_mask_bit(int p_bit) const{
+
+ return get_collision_mask()&(1<<p_bit);
+}
+
+
+void PhysicsBody::set_layer_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_layer_mask();
+ if (p_value)
+ mask|=1<<p_bit;
+ else
+ mask&=~(1<<p_bit);
+ set_layer_mask(mask);
+
+}
+
+bool PhysicsBody::get_layer_mask_bit(int p_bit) const{
+
+ return get_layer_mask()&(1<<p_bit);
+}
+
void PhysicsBody::add_collision_exception_with(Node* p_node) {
ERR_FAIL_NULL(p_node);
@@ -92,17 +136,42 @@ void PhysicsBody::remove_collision_exception_with(Node* p_node) {
PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(),physics_body->get_rid());
}
+void PhysicsBody::_set_layers(uint32_t p_mask) {
+ set_layer_mask(p_mask);
+ set_collision_mask(p_mask);
+}
+
+uint32_t PhysicsBody::_get_layers() const{
+
+ return get_layer_mask();
+}
void PhysicsBody::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"),&PhysicsBody::set_layer_mask);
ObjectTypeDB::bind_method(_MD("get_layer_mask"),&PhysicsBody::get_layer_mask);
- ADD_PROPERTY(PropertyInfo(Variant::INT,"layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask"));
+
+ ObjectTypeDB::bind_method(_MD("set_collision_mask","mask"),&PhysicsBody::set_collision_mask);
+ ObjectTypeDB::bind_method(_MD("get_collision_mask"),&PhysicsBody::get_collision_mask);
+
+ ObjectTypeDB::bind_method(_MD("set_collision_mask_bit","bit","value"),&PhysicsBody::set_collision_mask_bit);
+ ObjectTypeDB::bind_method(_MD("get_collision_mask_bit","bit"),&PhysicsBody::get_collision_mask_bit);
+
+ ObjectTypeDB::bind_method(_MD("set_layer_mask_bit","bit","value"),&PhysicsBody::set_layer_mask_bit);
+ ObjectTypeDB::bind_method(_MD("get_layer_mask_bit","bit"),&PhysicsBody::get_layer_mask_bit);
+
+ ObjectTypeDB::bind_method(_MD("_set_layers","mask"),&PhysicsBody::_set_layers);
+ ObjectTypeDB::bind_method(_MD("_get_layers"),&PhysicsBody::_get_layers);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"layers",PROPERTY_HINT_ALL_FLAGS,"",0),_SCS("_set_layers"),_SCS("_get_layers")); //for backwards compat
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"collision/layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"collision/mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_collision_mask"),_SCS("get_collision_mask"));
}
PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : CollisionObject( PhysicsServer::get_singleton()->body_create(p_mode), false) {
layer_mask=1;
+ collision_mask=1;
}
@@ -419,7 +488,10 @@ void RigidBody::_direct_state_changed(Object *p_state) {
set_global_transform(state->get_transform());
linear_velocity=state->get_linear_velocity();
angular_velocity=state->get_angular_velocity();
- sleeping=state->is_sleeping();
+ if(sleeping!=state->is_sleeping()) {
+ sleeping=state->is_sleeping();
+ emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed);
+ }
if (get_script_instance())
get_script_instance()->call("_integrate_forces",state);
set_ignore_transform_notification(false);
@@ -811,6 +883,7 @@ void RigidBody::_bind_methods() {
ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape")));
ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body")));
ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body")));
+ ADD_SIGNAL( MethodInfo("sleeping_state_changed"));
BIND_CONSTANT( MODE_STATIC );
BIND_CONSTANT( MODE_KINEMATIC );
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index da79d63f00..f95b4f017f 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -39,6 +39,11 @@ class PhysicsBody : public CollisionObject {
OBJ_TYPE(PhysicsBody,CollisionObject);
uint32_t layer_mask;
+ uint32_t collision_mask;
+
+ void _set_layers(uint32_t p_mask);
+ uint32_t _get_layers() const;
+
protected:
static void _bind_methods();
@@ -53,6 +58,15 @@ public:
void set_layer_mask(uint32_t p_mask);
uint32_t get_layer_mask() const;
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_layer_mask_bit(int p_bit, bool p_value);
+ bool get_layer_mask_bit(int p_bit) const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
void add_collision_exception_with(Node* p_node); //must be physicsbody
void remove_collision_exception_with(Node* p_node);
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index ab2c4fc8dc..1acda8d1f8 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -33,7 +33,7 @@
void RayCast::set_cast_to(const Vector3& p_point) {
cast_to=p_point;
- if (is_inside_tree() && get_tree()->is_editor_hint())
+ if (is_inside_tree() && (get_tree()->is_editor_hint() || get_tree()->is_debugging_collisions_hint()))
update_gizmo();
}
@@ -43,6 +43,26 @@ Vector3 RayCast::get_cast_to() const{
return cast_to;
}
+void RayCast::set_layer_mask(uint32_t p_mask) {
+
+ layer_mask=p_mask;
+}
+
+uint32_t RayCast::get_layer_mask() const {
+
+ return layer_mask;
+}
+
+void RayCast::set_type_mask(uint32_t p_mask) {
+
+ type_mask=p_mask;
+}
+
+uint32_t RayCast::get_type_mask() const {
+
+ return type_mask;
+}
+
bool RayCast::is_colliding() const{
return collided;
@@ -130,7 +150,7 @@ void RayCast::_notification(int p_what) {
PhysicsDirectSpaceState::RayResult rr;
- if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude)) {
+ if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude, layer_mask, type_mask)) {
collided=true;
against=rr.collider_id;
@@ -206,8 +226,16 @@ void RayCast::_bind_methods() {
ObjectTypeDB::bind_method(_MD("clear_exceptions"),&RayCast::clear_exceptions);
+ ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"),&RayCast::set_layer_mask);
+ ObjectTypeDB::bind_method(_MD("get_layer_mask"),&RayCast::get_layer_mask);
+
+ ObjectTypeDB::bind_method(_MD("set_type_mask","mask"),&RayCast::set_type_mask);
+ ObjectTypeDB::bind_method(_MD("get_type_mask"),&RayCast::get_type_mask);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"layer_mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask"));
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"type_mask",PROPERTY_HINT_FLAGS,"Static,Kinematic,Rigid,Character,Area"),_SCS("set_type_mask"),_SCS("get_type_mask"));
}
RayCast::RayCast() {
@@ -216,5 +244,7 @@ RayCast::RayCast() {
against=0;
collided=false;
against_shape=0;
+ layer_mask=1;
+ type_mask=PhysicsDirectSpaceState::TYPE_MASK_COLLISION;
cast_to=Vector3(0,-1,0);
}
diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h
index 520b4d5313..4f6514e61b 100644
--- a/scene/3d/ray_cast.h
+++ b/scene/3d/ray_cast.h
@@ -47,6 +47,9 @@ class RayCast : public Spatial {
Set<RID> exclude;
+ uint32_t layer_mask;
+ uint32_t type_mask;
+
protected:
void _notification(int p_what);
@@ -59,6 +62,12 @@ public:
void set_cast_to(const Vector3& p_point);
Vector3 get_cast_to() const;
+ void set_layer_mask(uint32_t p_mask);
+ uint32_t get_layer_mask() const;
+
+ void set_type_mask(uint32_t p_mask);
+ uint32_t get_type_mask() const;
+
bool is_colliding() const;
Object *get_collider() const;
int get_collider_shape() const;
diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp
index 2e22ab36d3..f01c2263fb 100644
--- a/scene/3d/scenario_fx.cpp
+++ b/scene/3d/scenario_fx.cpp
@@ -40,12 +40,17 @@ void WorldEnvironment::_notification(int p_what) {
WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding.");
}
get_world()->set_environment(environment);
+ add_to_group("_world_environment_"+itos(get_world()->get_scenario().get_id()));
+
}
} else if (p_what==NOTIFICATION_EXIT_WORLD) {
- if (environment.is_valid() && get_world()->get_environment()==environment)
+ if (environment.is_valid() && get_world()->get_environment()==environment) {
get_world()->set_environment(Ref<Environment>());
+ remove_from_group("_world_environment_"+itos(get_world()->get_scenario().get_id()));
+
+ }
}
}
@@ -53,6 +58,7 @@ void WorldEnvironment::set_environment(const Ref<Environment>& p_environment) {
if (is_inside_world() && environment.is_valid() && get_world()->get_environment()==environment) {
get_world()->set_environment(Ref<Environment>());
+ remove_from_group("_world_environment_"+itos(get_world()->get_scenario().get_id()));
//clean up
}
@@ -63,7 +69,11 @@ void WorldEnvironment::set_environment(const Ref<Environment>& p_environment) {
WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding.");
}
get_world()->set_environment(environment);
+ add_to_group("_world_environment_"+itos(get_world()->get_scenario().get_id()));
+
}
+
+ update_configuration_warning();
}
Ref<Environment> WorldEnvironment::get_environment() const {
@@ -71,6 +81,21 @@ Ref<Environment> WorldEnvironment::get_environment() const {
return environment;
}
+String WorldEnvironment::get_configuration_warning() const {
+
+ if (!is_visible() || !is_inside_tree() || !environment.is_valid())
+ return String();
+
+ List<Node*> nodes;
+ get_tree()->get_nodes_in_group("_world_environment_"+itos(get_world()->get_scenario().get_id()),&nodes);
+
+ if (nodes.size()>1) {
+ return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes).");
+ }
+
+ return String();
+}
+
void WorldEnvironment::_bind_methods() {
diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h
index a3c13e03a1..a73c455918 100644
--- a/scene/3d/scenario_fx.h
+++ b/scene/3d/scenario_fx.h
@@ -51,6 +51,8 @@ public:
void set_environment(const Ref<Environment>& p_environment);
Ref<Environment> get_environment() const;
+ String get_configuration_warning() const;
+
WorldEnvironment();
};
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index c2d318e8a7..6a9c655141 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -346,14 +346,14 @@ void Spatial::set_translation(const Vector3& p_translation) {
}
-void Spatial::set_rotation(const Vector3& p_euler){
+void Spatial::set_rotation(const Vector3& p_euler_rad){
if (data.dirty&DIRTY_VECTORS) {
data.scale=data.local_transform.basis.get_scale();
data.dirty&=~DIRTY_VECTORS;
}
- data.rotation=p_euler;
+ data.rotation=p_euler_rad;
data.dirty|=DIRTY_LOCAL;
_propagate_transform_changed(this);
if (data.notify_local_transform) {
@@ -361,6 +361,18 @@ void Spatial::set_rotation(const Vector3& p_euler){
}
}
+
+void Spatial::set_rotation_deg(const Vector3& p_euler_deg) {
+
+ set_rotation(p_euler_deg * Math_PI / 180.0);
+}
+
+void Spatial::_set_rotation_deg(const Vector3& p_euler_deg) {
+
+ WARN_PRINT("Deprecated method Spatial._set_rotation_deg(): This method was renamed to set_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted.");
+ set_rotation_deg(p_euler_deg);
+}
+
void Spatial::set_scale(const Vector3& p_scale){
if (data.dirty&DIRTY_VECTORS) {
@@ -381,6 +393,7 @@ Vector3 Spatial::get_translation() const{
return data.local_transform.origin;
}
+
Vector3 Spatial::get_rotation() const{
if (data.dirty&DIRTY_VECTORS) {
@@ -391,6 +404,20 @@ Vector3 Spatial::get_rotation() const{
return data.rotation;
}
+
+Vector3 Spatial::get_rotation_deg() const {
+
+ return get_rotation() * 180.0 / Math_PI;
+}
+
+// Kept for compatibility after rename to set_rotd.
+// Could be removed after a couple releases.
+Vector3 Spatial::_get_rotation_deg() const {
+
+ WARN_PRINT("Deprecated method Spatial._get_rotation_deg(): This method was renamed to get_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted.");
+ return get_rotation_deg();
+}
+
Vector3 Spatial::get_scale() const{
if (data.dirty&DIRTY_VECTORS) {
@@ -495,16 +522,6 @@ bool Spatial::is_set_as_toplevel() const{
return data.toplevel;
}
-void Spatial::_set_rotation_deg(const Vector3& p_deg) {
-
- set_rotation(p_deg * Math_PI / 180.0);
-}
-
-Vector3 Spatial::_get_rotation_deg() const {
-
- return get_rotation() * 180.0 / Math_PI;
-}
-
Ref<World> Spatial::get_world() const {
ERR_FAIL_COND_V(!is_inside_world(),Ref<World>());
@@ -722,8 +739,10 @@ void Spatial::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_transform"), &Spatial::get_transform);
ObjectTypeDB::bind_method(_MD("set_translation","translation"), &Spatial::set_translation);
ObjectTypeDB::bind_method(_MD("get_translation"), &Spatial::get_translation);
- ObjectTypeDB::bind_method(_MD("set_rotation","rotation"), &Spatial::set_rotation);
+ ObjectTypeDB::bind_method(_MD("set_rotation","rotation_rad"), &Spatial::set_rotation);
ObjectTypeDB::bind_method(_MD("get_rotation"), &Spatial::get_rotation);
+ ObjectTypeDB::bind_method(_MD("set_rotation_deg","rotation_deg"), &Spatial::set_rotation_deg);
+ ObjectTypeDB::bind_method(_MD("get_rotation_deg"), &Spatial::get_rotation_deg);
ObjectTypeDB::bind_method(_MD("set_scale","scale"), &Spatial::set_scale);
ObjectTypeDB::bind_method(_MD("get_scale"), &Spatial::get_scale);
ObjectTypeDB::bind_method(_MD("set_global_transform","global"), &Spatial::set_global_transform);
@@ -732,9 +751,11 @@ void Spatial::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_ignore_transform_notification","enabled"), &Spatial::set_ignore_transform_notification);
ObjectTypeDB::bind_method(_MD("set_as_toplevel","enable"), &Spatial::set_as_toplevel);
ObjectTypeDB::bind_method(_MD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel);
+ ObjectTypeDB::bind_method(_MD("get_world:World"), &Spatial::get_world);
+
+ // TODO: Obsolete those two methods (old name) properly (GH-4397)
ObjectTypeDB::bind_method(_MD("_set_rotation_deg","rotation_deg"), &Spatial::_set_rotation_deg);
ObjectTypeDB::bind_method(_MD("_get_rotation_deg"), &Spatial::_get_rotation_deg);
- ObjectTypeDB::bind_method(_MD("get_world:World"), &Spatial::get_world);
#ifdef TOOLS_ENABLED
ObjectTypeDB::bind_method(_MD("_update_gizmo"), &Spatial::_update_gizmo);
@@ -789,7 +810,7 @@ void Spatial::_bind_methods() {
//ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), _SCS("set_global_transform"), _SCS("get_global_transform") );
ADD_PROPERTYNZ( PropertyInfo(Variant::TRANSFORM,"transform/local",PROPERTY_HINT_NONE,""), _SCS("set_transform"), _SCS("get_transform") );
ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/translation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_translation"), _SCS("get_translation") );
- ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("_set_rotation_deg"), _SCS("_get_rotation_deg") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_rotation_deg"), _SCS("get_rotation_deg") );
ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation_rad",PROPERTY_HINT_NONE,"",0), _SCS("set_rotation"), _SCS("get_rotation") );
ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/scale",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_scale"), _SCS("get_scale") );
ADD_PROPERTYNO( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"), _SCS("_is_visible_") );
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
index 50123b2d81..fdc9f95f0b 100644
--- a/scene/3d/spatial.h
+++ b/scene/3d/spatial.h
@@ -110,7 +110,8 @@ class Spatial : public Node {
void _notify_dirty();
void _propagate_transform_changed(Spatial *p_origin);
- void _set_rotation_deg(const Vector3& p_deg);
+ // Deprecated, should be removed in a future version.
+ void _set_rotation_deg(const Vector3& p_euler_deg);
Vector3 _get_rotation_deg() const;
void _propagate_visibility_changed();
@@ -144,11 +145,13 @@ public:
Ref<World> get_world() const;
void set_translation(const Vector3& p_translation);
- void set_rotation(const Vector3& p_euler);
+ void set_rotation(const Vector3& p_euler_rad);
+ void set_rotation_deg(const Vector3& p_euler_deg);
void set_scale(const Vector3& p_scale);
Vector3 get_translation() const;
Vector3 get_rotation() const;
+ Vector3 get_rotation_deg() const;
Vector3 get_scale() const;
void set_transform(const Transform& p_transform);
diff --git a/scene/3d/spatial_sample_player.cpp b/scene/3d/spatial_sample_player.cpp
index 7114fd4b77..0df921f208 100644
--- a/scene/3d/spatial_sample_player.cpp
+++ b/scene/3d/spatial_sample_player.cpp
@@ -104,6 +104,7 @@ void SpatialSamplePlayer::set_sample_library(const Ref<SampleLibrary>& p_library
library=p_library;
_change_notify();
+ update_configuration_warning();
}
Ref<SampleLibrary> SpatialSamplePlayer::get_sample_library() const {
@@ -190,6 +191,16 @@ void SpatialSamplePlayer::stop_all() {
}
}
+String SpatialSamplePlayer::get_configuration_warning() const {
+
+ if (library.is_null()) {
+ return TTR("A SampleLibrary resource must be created or set in the 'samples' property in order for SpatialSamplePlayer to play sound.");
+ }
+
+ return String();
+}
+
+
void SpatialSamplePlayer::_bind_methods() {
diff --git a/scene/3d/spatial_sample_player.h b/scene/3d/spatial_sample_player.h
index 037cdc906a..257f6d0dc3 100644
--- a/scene/3d/spatial_sample_player.h
+++ b/scene/3d/spatial_sample_player.h
@@ -78,6 +78,7 @@ public:
void stop_voice(VoiceID p_voice);
void stop_all();
+ String get_configuration_warning() const;
SpatialSamplePlayer();
~SpatialSamplePlayer();
diff --git a/scene/3d/spatial_stream_player.cpp b/scene/3d/spatial_stream_player.cpp
index dfef0faf4b..11debb9bce 100644
--- a/scene/3d/spatial_stream_player.cpp
+++ b/scene/3d/spatial_stream_player.cpp
@@ -329,8 +329,8 @@ int SpatialStreamPlayer::get_buffering_msec() const{
void SpatialStreamPlayer::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&SpatialStreamPlayer::set_stream);
- ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&SpatialStreamPlayer::get_stream);
+ ObjectTypeDB::bind_method(_MD("set_stream","stream:AudioStream"),&SpatialStreamPlayer::set_stream);
+ ObjectTypeDB::bind_method(_MD("get_stream:AudioStream"),&SpatialStreamPlayer::get_stream);
ObjectTypeDB::bind_method(_MD("play","offset"),&SpatialStreamPlayer::play,DEFVAL(0));
ObjectTypeDB::bind_method(_MD("stop"),&SpatialStreamPlayer::stop);
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index c7d1249a07..da83e9b6d2 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -606,11 +606,11 @@ void AnimatedSprite3D::_draw() {
RID immediate = get_immediate();
VS::get_singleton()->immediate_clear(immediate);
- if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) {
+ if (!frames.is_valid() || !frames->get_frame_count(animation) || frame<0 || frame>=frames->get_frame_count(animation)) {
return;
}
- Ref<Texture> texture = frames->get_frame(frame);
+ Ref<Texture> texture = frames->get_frame(animation,frame);
if (!texture.is_valid())
return; //no texuture no life
Vector2 tsize = texture->get_size();
@@ -748,7 +748,7 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames>& p_sprite_frame
if (frames.is_valid())
frames->connect("changed",this,"_queue_update");
- if (!frames.is_valid() || frame >=frames->get_frame_count()) {
+ if (!frames.is_valid() || frame >=frames->get_frame_count(animation)) {
frame=0;
}
@@ -766,7 +766,7 @@ void AnimatedSprite3D::set_frame(int p_frame){
if (frames.is_null())
return;
- ERR_FAIL_INDEX(p_frame,frames->get_frame_count());
+ ERR_FAIL_INDEX(p_frame,frames->get_frame_count(animation));
if (frame==p_frame)
return;
@@ -783,11 +783,11 @@ int AnimatedSprite3D::get_frame() const{
Rect2 AnimatedSprite3D::get_item_rect() const {
- if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) {
+ if (!frames.is_valid() || !frames->get_frame_count(animation) || frame<0 || frame>=frames->get_frame_count(animation)) {
return Rect2(0,0,1,1);
}
- Ref<Texture> t = frames->get_frame(frame);
+ Ref<Texture> t = frames->get_frame(animation,frame);
if (t.is_null())
return Rect2(0,0,1,1);
Size2i s = t->get_size();
@@ -806,6 +806,8 @@ Rect2 AnimatedSprite3D::get_item_rect() const {
AnimatedSprite3D::AnimatedSprite3D() {
+ animation="current";
frame=0;
}
+
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index fe8e1f6ebf..d8e589556c 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -158,12 +158,14 @@ public:
// ~Sprite3D();
};
+
class AnimatedSprite3D : public SpriteBase3D {
OBJ_TYPE(AnimatedSprite3D,SpriteBase3D);
Ref<SpriteFrames> frames;
+ StringName animation;
int frame;
protected:
@@ -186,6 +188,7 @@ public:
// ~AnimatedSprite3D();
};
+
VARIANT_ENUM_CAST(SpriteBase3D::DrawFlags);
VARIANT_ENUM_CAST(SpriteBase3D::AlphaCutMode);
#endif // SPRITE_3D_H
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index ba30c118f0..e35ba11e84 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -936,7 +936,7 @@ void VehicleBody::_direct_state_changed(Object *p_state) {
wheel.m_deltaRotation *= real_t(0.99);//damping of rotation when not in contact
}
-
+ linear_velocity = s->get_linear_velocity();
}
void VehicleBody::set_mass(real_t p_mass) {
@@ -990,6 +990,10 @@ float VehicleBody::get_steering() const{
return m_steeringValue;
}
+Vector3 VehicleBody::get_linear_velocity()
+{
+ return linear_velocity;
+}
void VehicleBody::_bind_methods(){
@@ -1008,6 +1012,8 @@ void VehicleBody::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_steering","steering"),&VehicleBody::set_steering);
ObjectTypeDB::bind_method(_MD("get_steering"),&VehicleBody::get_steering);
+ ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&VehicleBody::get_linear_velocity);
+
ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&VehicleBody::_direct_state_changed);
ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/engine_force",PROPERTY_HINT_RANGE,"0.00,1024.0,0.01"),_SCS("set_engine_force"),_SCS("get_engine_force"));
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
index 285cca142d..b6ad88f15e 100644
--- a/scene/3d/vehicle_body.h
+++ b/scene/3d/vehicle_body.h
@@ -178,6 +178,7 @@ public:
void set_steering(float p_steering);
float get_steering() const;
+ Vector3 get_linear_velocity();
VehicleBody();
};
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 32958112e5..2399bee539 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1013,6 +1013,8 @@ void AnimationPlayer::play(const StringName& p_name, float p_custom_blend, float
_set_process(true); // always process when starting an animation
playing = true;
+ emit_signal(SceneStringNames::get_singleton()->animation_started, c.assigned);
+
if (is_inside_tree() && get_tree()->is_editor_hint())
return; // no next in this case
@@ -1297,6 +1299,9 @@ void AnimationPlayer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_animation:Animation","name"),&AnimationPlayer::get_animation);
ObjectTypeDB::bind_method(_MD("get_animation_list"),&AnimationPlayer::_get_animation_list);
+ ObjectTypeDB::bind_method(_MD("animation_set_next", "anim_from", "anim_to"), &AnimationPlayer::animation_set_next);
+ ObjectTypeDB::bind_method(_MD("animation_get_next", "anim_from"), &AnimationPlayer::animation_get_next);
+
ObjectTypeDB::bind_method(_MD("set_blend_time","anim_from","anim_to","sec"),&AnimationPlayer::set_blend_time);
ObjectTypeDB::bind_method(_MD("get_blend_time","anim_from","anim_to"),&AnimationPlayer::get_blend_time);
@@ -1347,6 +1352,7 @@ void AnimationPlayer::_bind_methods() {
ADD_SIGNAL( MethodInfo("finished") );
ADD_SIGNAL( MethodInfo("animation_changed", PropertyInfo(Variant::STRING,"old_name"), PropertyInfo(Variant::STRING,"new_name")) );
+ ADD_SIGNAL( MethodInfo("animation_started", PropertyInfo(Variant::STRING,"name")) );
BIND_CONSTANT( ANIMATION_PROCESS_FIXED );
BIND_CONSTANT( ANIMATION_PROCESS_IDLE );
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 9dcad8a533..211c5961b0 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -139,6 +139,11 @@ bool AnimationTreePlayer::_set(const StringName& p_name, const Variant& p_value)
animation_node_set_master_animation(id,node.get_valid("from"));
else
animation_node_set_animation(id,node.get_valid("animation"));
+ Array filters= node.get_valid("filter");
+ for(int i=0;i<filters.size();i++) {
+
+ animation_node_set_filter_path(id,filters[i],true);
+ }
} break;
case NODE_ONESHOT: {
@@ -276,6 +281,15 @@ bool AnimationTreePlayer::_get(const StringName& p_name,Variant &r_ret) const {
} else {
node["animation"]=an->animation;
}
+ Array k;
+ List<NodePath> keys;
+ an->filter.get_key_list(&keys);
+ k.resize(keys.size());
+ int i=0;
+ for(List<NodePath>::Element *E=keys.front();E;E=E->next()) {
+ k[i++]=E->get();
+ }
+ node["filter"]=k;
} break;
case NODE_ONESHOT: {
OneShotNode *osn = static_cast<OneShotNode*>(n);
@@ -432,20 +446,19 @@ void AnimationTreePlayer::_notification(int p_what) {
}
-float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode **r_prev_anim,float p_weight, float p_time, bool switched, bool p_seek,const HashMap<NodePath,bool> *p_filter, float p_reverse_weight) {
+float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode **r_prev_anim,float p_weight, float p_time, bool p_seek,const HashMap<NodePath,bool> *p_filter, float p_reverse_weight) {
ERR_FAIL_COND_V(!node_map.has(p_node), 0);
NodeBase *nb=node_map[p_node];
//transform to seconds...
-
switch(nb->type) {
case NODE_OUTPUT: {
NodeOut *on = static_cast<NodeOut*>(nb);
- return _process_node(on->inputs[0].node,r_prev_anim,p_weight,p_time,switched,p_seek);
+ return _process_node(on->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek);
} break;
case NODE_ANIMATION: {
@@ -464,7 +477,7 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
an->time=p_time;
an->step=0;
} else {
- an->time+=p_time;
+ an->time=MAX(0,an->time+p_time);
an->step=p_time;
}
@@ -479,20 +492,15 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
an->time=anim_size;
}
- if (switched && an->time >= anim_size) {
- an->time = 0.0;
- }
an->skip=true;
for (List<AnimationNode::TrackRef>::Element *E=an->tref.front();E;E=E->next()) {
-
- if (p_filter && p_filter->has(an->animation->track_get_path(E->get().local_track))) {
-
- if (p_reverse_weight<0)
- E->get().weight=0;
- else
- E->get().weight=p_reverse_weight;
-
+ NodePath track_path = an->animation->track_get_path(E->get().local_track);
+ if (p_filter && p_filter->has(track_path)) {
+ E->get().weight = MAX(0, p_reverse_weight);
+ } else if(an->filter.has(track_path)) {
+ E->get().weight = 0;
+ E->get().track->skip = true;
} else {
E->get().weight=p_weight;
}
@@ -523,13 +531,17 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
if (!osn->active) {
//make it as if this node doesn't exist, pass input 0 by.
- return _process_node(osn->inputs[0].node,r_prev_anim,p_weight,p_time,switched,p_seek,p_filter,p_reverse_weight);
+ return _process_node(osn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight);
}
+ float os_seek = p_seek;
+
if (p_seek)
osn->time=p_time;
- if (osn->start)
+ if (osn->start) {
osn->time=0;
+ os_seek = true;
+ }
float blend;
@@ -551,18 +563,17 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
float main_rem;
float os_rem;
+ float os_reverse_weight = p_reverse_weight;
if (!osn->filter.empty()) {
-
- main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,switched,p_seek,&osn->filter,p_weight);
- os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,osn->start,p_seek,&osn->filter,-1);
-
- } else {
-
- main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,switched,p_seek);
- os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,osn->start,p_seek);
+ p_filter = &osn->filter;
+ p_reverse_weight = p_weight;
+ os_reverse_weight = -1;
}
+ main_rem = _process_node(osn->inputs[0].node,r_prev_anim,(osn->mix?p_weight:p_weight*(1.0-blend)),p_time,p_seek,p_filter,p_reverse_weight);
+ os_rem = _process_node(osn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,os_seek,p_filter,os_reverse_weight);
+
if (osn->start) {
osn->remaining=os_rem;
osn->start=false;
@@ -570,8 +581,8 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
if (!p_seek) {
osn->time+=p_time;
- osn->remaining-=p_time;
- if (osn->remaining<0)
+ osn->remaining=os_rem;
+ if (osn->remaining<=0)
osn->active=false;
}
@@ -581,8 +592,8 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
MixNode *mn = static_cast<MixNode*>(nb);
- float rem = _process_node(mn->inputs[0].node,r_prev_anim,p_weight,p_time,switched,p_seek,p_filter,p_reverse_weight);
- _process_node(mn->inputs[1].node,r_prev_anim,p_weight*mn->amount,p_time,switched,p_seek,p_filter,p_reverse_weight);
+ float rem = _process_node(mn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight);
+ _process_node(mn->inputs[1].node,r_prev_anim,p_weight*mn->amount,p_time,p_seek,p_filter,p_reverse_weight);
return rem;
} break;
@@ -593,12 +604,12 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
float rem;
if (!bn->filter.empty()) {
- rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,switched,p_seek,&bn->filter,p_weight);
- _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,switched,p_seek,&bn->filter,-1);
+ rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,&bn->filter,p_weight);
+ _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,&bn->filter,-1);
} else {
- rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,switched,p_seek,p_filter,p_reverse_weight*(1.0-bn->value));
- _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,switched,p_seek,p_filter,p_reverse_weight*bn->value);
+ rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value));
+ _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value,p_time,p_seek,p_filter,p_reverse_weight*bn->value);
}
return rem;
@@ -618,19 +629,19 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
upper_blend = bn->value;
}
- rem = _process_node(bn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,switched,p_seek,p_filter,p_reverse_weight*blend);
- _process_node(bn->inputs[2].node,r_prev_anim,p_weight*upper_blend,p_time,switched,p_seek,p_filter,p_reverse_weight*upper_blend);
- _process_node(bn->inputs[0].node,r_prev_anim,p_weight*lower_blend,p_time,switched,p_seek,p_filter,p_reverse_weight*lower_blend);
+ rem = _process_node(bn->inputs[1].node,r_prev_anim,p_weight*blend,p_time,p_seek,p_filter,p_reverse_weight*blend);
+ _process_node(bn->inputs[2].node,r_prev_anim,p_weight*upper_blend,p_time,p_seek,p_filter,p_reverse_weight*upper_blend);
+ _process_node(bn->inputs[0].node,r_prev_anim,p_weight*lower_blend,p_time,p_seek,p_filter,p_reverse_weight*lower_blend);
return rem;
} break;
case NODE_BLEND4: {
Blend4Node *bn = static_cast<Blend4Node*>(nb);
- float rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value.x),p_time,switched,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.x));
- _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value.x,p_time,switched,p_seek,p_filter,p_reverse_weight*bn->value.x);
- float rem2 = _process_node(bn->inputs[2].node,r_prev_anim,p_weight*(1.0-bn->value.y),p_time,switched,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.y));
- _process_node(bn->inputs[3].node,r_prev_anim,p_weight*bn->value.y,p_time,switched,p_seek,p_filter,p_reverse_weight*bn->value.y);
+ float rem = _process_node(bn->inputs[0].node,r_prev_anim,p_weight*(1.0-bn->value.x),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.x));
+ _process_node(bn->inputs[1].node,r_prev_anim,p_weight*bn->value.x,p_time,p_seek,p_filter,p_reverse_weight*bn->value.x);
+ float rem2 = _process_node(bn->inputs[2].node,r_prev_anim,p_weight*(1.0-bn->value.y),p_time,p_seek,p_filter,p_reverse_weight*(1.0-bn->value.y));
+ _process_node(bn->inputs[3].node,r_prev_anim,p_weight*bn->value.y,p_time,p_seek,p_filter,p_reverse_weight*bn->value.y);
return MAX(rem,rem2);
@@ -639,9 +650,9 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
TimeScaleNode *tsn = static_cast<TimeScaleNode*>(nb);
float rem;
if (p_seek)
- rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,switched,true,p_filter,p_reverse_weight);
+ rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,true,p_filter,p_reverse_weight);
else
- rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time*tsn->scale,switched,false,p_filter,p_reverse_weight);
+ rem = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time*tsn->scale,false,p_filter,p_reverse_weight);
if (tsn->scale == 0)
return INFINITY;
else
@@ -651,68 +662,58 @@ float AnimationTreePlayer::_process_node(const StringName& p_node,AnimationNode
case NODE_TIMESEEK: {
TimeSeekNode *tsn = static_cast<TimeSeekNode*>(nb);
- if (tsn->seek_pos>=0) {
+ if (tsn->seek_pos>=0 && !p_seek) {
- float res = _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,tsn->seek_pos,switched,true,p_filter,p_reverse_weight);
- tsn->seek_pos=-1;
- return res;
+ p_time = tsn->seek_pos;
+ p_seek = true;
+ }
+ tsn->seek_pos=-1;
- } else
- return _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,switched,p_seek);
+ return _process_node(tsn->inputs[0].node,r_prev_anim,p_weight,p_time,p_seek);
} break;
case NODE_TRANSITION: {
TransitionNode *tn = static_cast<TransitionNode*>(nb);
- if (tn->prev<0) {
+ if (tn->prev<0) { // process current animation, check for transition
- float rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight,p_time,switched,p_seek,p_filter,p_reverse_weight);
+ float rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight,p_time,p_seek,p_filter,p_reverse_weight);
if (p_seek)
tn->time=p_time;
else
tn->time+=p_time;
- if (tn->input_data[tn->current].auto_advance && rem < tn->xfade) {
+ if (tn->input_data[tn->current].auto_advance && rem <= tn->xfade) {
- tn->prev=tn->current;
- tn->current++;
- if (tn->current>=tn->inputs.size())
- tn->current=0;
- tn->prev_xfading=tn->xfade;
- tn->prev_time=tn->time;
- tn->time=0;
- tn->switched=true;
+ tn->set_current((tn->current+1) % tn->inputs.size());
}
return rem;
- } else {
+ } else { // cross-fading from tn->prev to tn->current
float blend = tn->xfade? (tn->prev_xfading/tn->xfade) : 1;
float rem;
- if (!p_seek && tn->switched) { //just switched
+ if (!p_seek && tn->switched) { //just switched, seek to start of current
- rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),0,true,true,p_filter,p_reverse_weight*(1.0-blend));
+ rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),0,true,p_filter,p_reverse_weight*(1.0-blend));
} else {
- rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),p_time,switched,p_seek,p_filter,p_reverse_weight*(1.0-blend));
+ rem = _process_node(tn->inputs[tn->current].node,r_prev_anim,p_weight*(1.0-blend),p_time,p_seek,p_filter,p_reverse_weight*(1.0-blend));
}
tn->switched=false;
- //if (!p_seek)
-
-
- if (p_seek) {
- _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,0,true,false,p_filter,p_reverse_weight*blend);
+ if (p_seek) { // don't seek prev animation
+ _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,0,false,p_filter,p_reverse_weight*blend);
tn->time=p_time;
} else {
- _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,p_time,switched,false,p_filter,p_reverse_weight*blend);
+ _process_node(tn->inputs[tn->prev].node,r_prev_anim,p_weight*blend,p_time,false,p_filter,p_reverse_weight*blend);
tn->time+=p_time;
tn->prev_xfading-=p_time;
if (tn->prev_xfading<0) {
@@ -749,10 +750,10 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
AnimationNode *prev=NULL;
if (reset_request) {
- _process_node(out_name,&prev, 1.0, 0, true, true );
+ _process_node(out_name,&prev, 1.0, 0, true);
reset_request=false;
} else
- _process_node(out_name,&prev, 1.0, p_delta, false, false );
+ _process_node(out_name,&prev, 1.0, p_delta);
if (dirty_caches) {
//some animation changed.. ignore this pass
@@ -775,9 +776,10 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
t.scale.y=0;
t.scale.z=0;
- Variant value = t.node->get(t.property);
- value.zero();
- t.node->set(t.property, value);
+ t.value = t.object->get(t.property);
+ t.value.zero();
+
+ t.skip = false;
}
@@ -824,18 +826,11 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
case Animation::TYPE_VALUE: { ///< Set a value in a property, can be interpolated.
if (a->value_track_is_continuous(tr.local_track)) {
- Variant blended, value = a->value_track_interpolate(tr.local_track,anim_list->time);
- Variant::blend(tr.track->node->get(tr.track->property),value,blend,blended);
- tr.track->node->set(tr.track->property,blended);
+ Variant value = a->value_track_interpolate(tr.local_track,anim_list->time);
+ Variant::blend(tr.track->value,value,blend,tr.track->value);
} else {
-
- List<int> indices;
- a->value_track_get_key_indices(tr.local_track,anim_list->time,anim_list->step,&indices);
- for(List<int>::Element *E=indices.front();E;E=E->next()) {
-
- Variant value = a->track_get_key_value(tr.local_track,E->get());
- tr.track->node->set(tr.track->property,value);
- }
+ int index = a->track_find_key(tr.local_track,anim_list->time);
+ tr.track->value = a->track_get_key_value(tr.local_track, index);
}
} break;
case Animation::TYPE_METHOD: { ///< Call any method on a specific node.
@@ -847,7 +842,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
StringName method = a->method_track_get_name(tr.local_track,E->get());
Vector<Variant> args=a->method_track_get_params(tr.local_track,E->get());
args.resize(VARIANT_ARG_MAX);
- tr.track->node->call(method,args[0],args[1],args[2],args[3],args[4]);
+ tr.track->object->call(method,args[0],args[1],args[2],args[3],args[4]);
}
} break;
}
@@ -864,11 +859,13 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
Track &t = E->get();
- if (!t.node)
+ if (t.skip || !t.object)
continue;
- if(t.property) // value track; was applied in step 2
+ if(t.property) { // value track
+ t.object->set(t.property,t.value);
continue;
+ }
Transform xform;
xform.basis=t.rot;
@@ -993,6 +990,24 @@ void AnimationTreePlayer::animation_node_set_master_animation(const StringName&
}
+void AnimationTreePlayer::animation_node_set_filter_path(const StringName& p_node,const NodePath& p_track_path,bool p_filter) {
+
+ GET_NODE( NODE_ANIMATION, AnimationNode );
+
+ if (p_filter)
+ n->filter[p_track_path]=true;
+ else
+ n->filter.erase(p_track_path);
+
+}
+
+void AnimationTreePlayer::animation_node_set_get_filtered_paths(const StringName& p_node,List<NodePath> *r_paths) const{
+
+ GET_NODE( NODE_ANIMATION, AnimationNode );
+
+ n->filter.get_key_list(r_paths);
+}
+
void AnimationTreePlayer::oneshot_node_set_fadein_time(const StringName& p_node,float p_time) {
GET_NODE( NODE_ONESHOT, OneShotNode );
@@ -1158,21 +1173,24 @@ void AnimationTreePlayer::transition_node_set_xfade_time(const StringName& p_nod
n->xfade=p_time;
}
+void AnimationTreePlayer::TransitionNode::set_current(int p_current) {
+ ERR_FAIL_INDEX(p_current,inputs.size());
-void AnimationTreePlayer::transition_node_set_current(const StringName& p_node, int p_current) {
-
- GET_NODE( NODE_TRANSITION, TransitionNode );
- ERR_FAIL_INDEX(p_current,n->inputs.size());
-
- if (n->current==p_current)
+ if (current==p_current)
return;
- n->prev=n->current;
- n->prev_xfading=n->xfade;
- n->prev_time=n->time;
- n->time=0;
- n->current=p_current;
+ prev=current;
+ prev_xfading=xfade;
+ prev_time=time;
+ time=0;
+ current=p_current;
+ switched=true;
+}
+void AnimationTreePlayer::transition_node_set_current(const StringName& p_node, int p_current) {
+
+ GET_NODE( NODE_TRANSITION, TransitionNode );
+ n->set_current(p_current);
}
@@ -1217,6 +1235,12 @@ String AnimationTreePlayer::animation_node_get_master_animation(const StringName
}
+bool AnimationTreePlayer::animation_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const {
+
+ GET_NODE_V(NODE_ANIMATION, AnimationNode, 0 );
+ return n->filter.has(p_path);
+}
+
float AnimationTreePlayer::oneshot_node_get_fadein_time(const StringName& p_node) const {
@@ -1500,7 +1524,8 @@ AnimationTreePlayer::Track* AnimationTreePlayer::_find_track(const NodePath& p_p
Node *parent=get_node(base_path);
ERR_FAIL_COND_V(!parent,NULL);
- Node *child=parent->get_node(p_path);
+ RES resource;
+ Node *child=parent->get_node_and_resource(p_path,resource);
if (!child) {
String err = "Animation track references unknown Node: '"+String(p_path)+"'.";
WARN_PRINT(err.ascii().get_data());
@@ -1528,7 +1553,7 @@ AnimationTreePlayer::Track* AnimationTreePlayer::_find_track(const NodePath& p_p
Track tr;
tr.id=id;
- tr.node=child;
+ tr.object=resource.is_valid()?(Object*)resource.ptr():(Object*)child;
tr.skeleton=child->cast_to<Skeleton>();
tr.spatial=child->cast_to<Spatial>();
tr.bone_idx=bone_idx;
@@ -1606,6 +1631,7 @@ void AnimationTreePlayer::set_active(bool p_active) {
active = p_active;
processing = active;
+ reset_request = p_active;
_set_process(processing, true);
}
@@ -1623,7 +1649,7 @@ AnimationTreePlayer::ConnectError AnimationTreePlayer::get_last_error() const {
void AnimationTreePlayer::reset() {
- reset_request=false;
+ reset_request=true;
}
@@ -1755,6 +1781,7 @@ void AnimationTreePlayer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("animation_node_set_master_animation","id","source"),&AnimationTreePlayer::animation_node_set_master_animation);
ObjectTypeDB::bind_method(_MD("animation_node_get_master_animation","id"),&AnimationTreePlayer::animation_node_get_master_animation);
+ ObjectTypeDB::bind_method(_MD("animation_node_set_filter_path","id","path","enable"),&AnimationTreePlayer::animation_node_set_filter_path);
ObjectTypeDB::bind_method(_MD("oneshot_node_set_fadein_time","id","time_sec"),&AnimationTreePlayer::oneshot_node_set_fadein_time);
ObjectTypeDB::bind_method(_MD("oneshot_node_get_fadein_time","id"),&AnimationTreePlayer::oneshot_node_get_fadein_time);
@@ -1860,12 +1887,11 @@ AnimationTreePlayer::AnimationTreePlayer() {
out_name="out";
out->pos=Point2(40,40);
node_map.insert( out_name , out);
- AnimationProcessMode animation_process_mode;
animation_process_mode = ANIMATION_PROCESS_IDLE;
processing = false;
active=false;
dirty_caches=true;
- reset_request=false;
+ reset_request=true;
last_error=CONNECT_INCOMPLETE;
base_path=String("..");
}
diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h
index 0fec9a9551..0e78281e4c 100644
--- a/scene/animation/animation_tree_player.h
+++ b/scene/animation/animation_tree_player.h
@@ -99,7 +99,7 @@ private:
struct Track {
uint32_t id;
- Node *node;
+ Object *object;
Spatial* spatial;
Skeleton *skeleton;
int bone_idx;
@@ -109,6 +109,9 @@ private:
Quat rot;
Vector3 scale;
+ Variant value;
+
+ bool skip;
};
@@ -160,6 +163,9 @@ private:
float step;
String from;
bool skip;
+
+ HashMap<NodePath,bool> filter;
+
AnimationNode() { type=NODE_ANIMATION; next=NULL; last_version=0; skip=false; }
};
@@ -246,6 +252,7 @@ private:
float xfade;
TransitionNode() { type=NODE_TRANSITION; xfade=0; inputs.resize(1); input_data.resize(1); current=0; prev=-1; prev_time=0; prev_xfading=0; switched=false; }
+ void set_current(int p_current);
};
@@ -267,7 +274,7 @@ private:
Map<StringName,NodeBase*> node_map;
// return time left to finish animation
- float _process_node(const StringName& p_node,AnimationNode **r_prev_anim, float p_weight,float p_step, bool switched, bool p_seek=false,const HashMap<NodePath,bool> *p_filter=NULL, float p_reverse_weight=0);
+ float _process_node(const StringName& p_node,AnimationNode **r_prev_anim, float p_weight,float p_step, bool p_seek=false,const HashMap<NodePath,bool> *p_filter=NULL, float p_reverse_weight=0);
void _process_animation(float p_delta);
bool reset_request;
@@ -307,6 +314,10 @@ public:
void animation_node_set_master_animation(const StringName& p_node,const String& p_master_animation);
String animation_node_get_master_animation(const StringName& p_node) const;
+ void animation_node_set_filter_path(const StringName& p_node,const NodePath& p_filter,bool p_enable);
+ void animation_node_set_get_filtered_paths(const StringName& p_node,List<NodePath> *r_paths) const;
+ bool animation_node_is_path_filtered(const StringName& p_node,const NodePath& p_path) const;
+
/* ONE SHOT NODE */
void oneshot_node_set_fadein_time(const StringName& p_node,float p_time);
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 7edd57603b..6f6f5d3aff 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -1045,6 +1045,7 @@ bool Tween::interpolate_property(Object *p_object
if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
@@ -1104,6 +1105,7 @@ bool Tween::interpolate_method(Object *p_object
if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
@@ -1154,7 +1156,9 @@ bool Tween::interpolate_callback(Object *p_object
);
return true;
}
+
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_times_in_sec < 0, false);
ERR_EXPLAIN("Object has no callback named: %s" + p_callback);
@@ -1219,6 +1223,7 @@ bool Tween::interpolate_deferred_callback(Object *p_object
return true;
}
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_times_in_sec < 0, false);
ERR_EXPLAIN("Object has no callback named: %s" + p_callback);
@@ -1291,7 +1296,9 @@ bool Tween::follow_property(Object *p_object
if(p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_target == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_target), false);
ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
@@ -1357,7 +1364,9 @@ bool Tween::follow_method(Object *p_object
if(p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_target == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_target), false);
ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
@@ -1424,7 +1433,9 @@ bool Tween::targeting_property(Object *p_object
if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_initial), false);
ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
@@ -1495,7 +1506,9 @@ bool Tween::targeting_method(Object *p_object
if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
ERR_FAIL_COND_V(p_object == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_object), false);
ERR_FAIL_COND_V(p_initial == NULL, false);
+ ERR_FAIL_COND_V(!ObjectDB::instance_validate(p_initial), false);
ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
diff --git a/scene/audio/sample_player.cpp b/scene/audio/sample_player.cpp
index d7605ed1a9..b4a237c60f 100644
--- a/scene/audio/sample_player.cpp
+++ b/scene/audio/sample_player.cpp
@@ -600,6 +600,14 @@ float SamplePlayer::get_default_reverb() const {
return _default.reverb_send;
}
+String SamplePlayer::get_configuration_warning() const {
+
+ if (library.is_null()) {
+ return TTR("A SampleLibrary resource must be created or set in the 'samples' property in order for SamplePlayer to play sound.");
+ }
+
+ return String();
+}
void SamplePlayer::_bind_methods() {
diff --git a/scene/audio/sample_player.h b/scene/audio/sample_player.h
index 1821c671dc..833fac3868 100644
--- a/scene/audio/sample_player.h
+++ b/scene/audio/sample_player.h
@@ -188,6 +188,8 @@ public:
ReverbRoomType get_default_reverb_room() const;
float get_default_reverb() const;
+ String get_configuration_warning() const;
+
SamplePlayer();
~SamplePlayer();
};
diff --git a/scene/audio/stream_player.cpp b/scene/audio/stream_player.cpp
index c1799ec12c..050e945c8f 100644
--- a/scene/audio/stream_player.cpp
+++ b/scene/audio/stream_player.cpp
@@ -342,8 +342,8 @@ int StreamPlayer::get_buffering_msec() const{
void StreamPlayer::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&StreamPlayer::set_stream);
- ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&StreamPlayer::get_stream);
+ ObjectTypeDB::bind_method(_MD("set_stream","stream:AudioStream"),&StreamPlayer::set_stream);
+ ObjectTypeDB::bind_method(_MD("get_stream:AudioStream"),&StreamPlayer::get_stream);
ObjectTypeDB::bind_method(_MD("play","offset"),&StreamPlayer::play,DEFVAL(0));
ObjectTypeDB::bind_method(_MD("stop"),&StreamPlayer::stop);
@@ -388,7 +388,7 @@ void StreamPlayer::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/paused"), _SCS("set_paused"), _SCS("is_paused") );
- ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/loop_restart_time"), _SCS("set_loop_restart_time"), _SCS("get_loop_restart_time") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/loop_restart_time"), _SCS("set_loop_restart_time"), _SCS("get_loop_restart_time") );
ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/buffering_ms"), _SCS("set_buffering_msec"), _SCS("get_buffering_msec") );
ADD_SIGNAL(MethodInfo("finished"));
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 3bcc60b86a..2200cac5da 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -289,7 +289,7 @@ void BaseButton::set_disabled(bool p_disabled) {
if (p_disabled)
set_focus_mode(FOCUS_NONE);
else
- set_focus_mode(FOCUS_ALL);
+ set_focus_mode(enabled_focus_mode);
}
bool BaseButton::is_disabled() const {
@@ -377,12 +377,56 @@ bool BaseButton::get_click_on_press() const {
return status.click_on_press;
}
+void BaseButton::set_enabled_focus_mode(FocusMode p_mode) {
+ enabled_focus_mode = p_mode;
+ if (!status.disabled) {
+ set_focus_mode( p_mode );
+ }
+}
+
+Control::FocusMode BaseButton::get_enabled_focus_mode() const {
+
+ return enabled_focus_mode;
+}
+
+void BaseButton::set_shortcut(const Ref<ShortCut>& p_shortcut) {
+
+ if (shortcut.is_null() == p_shortcut.is_null())
+ return;
+
+ shortcut=p_shortcut;
+ set_process_unhandled_input(shortcut.is_valid());
+}
+Ref<ShortCut> BaseButton:: get_shortcut() const {
+ return shortcut;
+}
+
+void BaseButton::_unhandled_input(InputEvent p_event) {
+
+ if (!is_disabled() && is_visible() && shortcut.is_valid() && shortcut->is_shortcut(p_event)) {
+ if (is_toggle_mode()) {
+ set_pressed(!is_pressed());
+ emit_signal("toggled",is_pressed());
+ }
+
+ emit_signal("pressed");
+ }
+}
+
+String BaseButton::get_tooltip(const Point2& p_pos) const {
+
+ String tooltip=Control::get_tooltip(p_pos);
+ if (shortcut.is_valid() && shortcut->is_valid())
+ tooltip+=" ("+shortcut->get_as_text()+")";
+ return tooltip;
+}
void BaseButton::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_input_event"),&BaseButton::_input_event);
+ ObjectTypeDB::bind_method(_MD("_unhandled_input"),&BaseButton::_unhandled_input);
ObjectTypeDB::bind_method(_MD("set_pressed","pressed"),&BaseButton::set_pressed);
ObjectTypeDB::bind_method(_MD("is_pressed"),&BaseButton::is_pressed);
ObjectTypeDB::bind_method(_MD("is_hovered"),&BaseButton::is_hovered);
@@ -393,6 +437,10 @@ void BaseButton::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_click_on_press","enable"),&BaseButton::set_click_on_press);
ObjectTypeDB::bind_method(_MD("get_click_on_press"),&BaseButton::get_click_on_press);
ObjectTypeDB::bind_method(_MD("get_draw_mode"),&BaseButton::get_draw_mode);
+ ObjectTypeDB::bind_method(_MD("set_enabled_focus_mode","mode"),&BaseButton::set_enabled_focus_mode);
+ ObjectTypeDB::bind_method(_MD("get_enabled_focus_mode"),&BaseButton::get_enabled_focus_mode);
+ ObjectTypeDB::bind_method(_MD("set_shortcut","shortcut"),&BaseButton::set_shortcut);
+ ObjectTypeDB::bind_method(_MD("get_shortcut"),&BaseButton::get_shortcut);
BIND_VMETHOD(MethodInfo("_pressed"));
BIND_VMETHOD(MethodInfo("_toggled",PropertyInfo(Variant::BOOL,"pressed")));
@@ -404,6 +452,8 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "toggle_mode"), _SCS("set_toggle_mode"), _SCS("is_toggle_mode"));
ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "is_pressed"), _SCS("set_pressed"), _SCS("is_pressed"));
ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "click_on_press"), _SCS("set_click_on_press"), _SCS("get_click_on_press"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT,"enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All" ), _SCS("set_enabled_focus_mode"), _SCS("get_enabled_focus_mode") );
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "shortcut",PROPERTY_HINT_RESOURCE_TYPE,"ShortCut"), _SCS("set_shortcut"), _SCS("get_shortcut"));
BIND_CONSTANT( DRAW_NORMAL );
@@ -424,6 +474,7 @@ BaseButton::BaseButton() {
status.click_on_press=false;
status.pressing_button=0;
set_focus_mode( FOCUS_ALL );
+ enabled_focus_mode = FOCUS_ALL;
group=NULL;
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 9a5213d971..0056b00f33 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -42,6 +42,8 @@ class BaseButton : public Control {
OBJ_TYPE( BaseButton, Control );
bool toggle_mode;
+ FocusMode enabled_focus_mode;
+ Ref<ShortCut> shortcut;
struct Status {
@@ -56,6 +58,7 @@ class BaseButton : public Control {
} status;
+
ButtonGroup *group;
@@ -68,6 +71,7 @@ protected:
virtual void toggled(bool p_pressed);
static void _bind_methods();
virtual void _input_event(InputEvent p_event);
+ virtual void _unhandled_input(InputEvent p_event);
void _notification(int p_what);
public:
@@ -97,6 +101,13 @@ public:
void set_click_on_press(bool p_click_on_press);
bool get_click_on_press() const;
+ void set_enabled_focus_mode(FocusMode p_mode);
+ FocusMode get_enabled_focus_mode() const;
+
+ void set_shortcut(const Ref<ShortCut>& p_shortcut);
+ Ref<ShortCut> get_shortcut() const;
+
+ virtual String get_tooltip(const Point2& p_pos) const;
BaseButton();
~BaseButton();
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index 6fff90809e..a6ffc30a83 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -280,6 +280,7 @@ BoxContainer::AlignMode BoxContainer::get_alignment() const {
void BoxContainer::add_spacer(bool p_begin) {
Control *c = memnew( Control );
+ c->set_stop_mouse(false);
if (vertical)
c->set_v_size_flags(SIZE_EXPAND_FILL);
else
@@ -300,6 +301,7 @@ BoxContainer::BoxContainer(bool p_vertical) {
void BoxContainer::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("add_spacer","begin"),&BoxContainer::add_spacer);
ObjectTypeDB::bind_method(_MD("get_alignment"),&BoxContainer::get_alignment);
ObjectTypeDB::bind_method(_MD("set_alignment","alignment"),&BoxContainer::set_alignment);
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 0f3f762ba1..579f6e08c9 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -213,7 +213,6 @@ Button::TextAlign Button::get_text_align() const {
return align;
}
-
void Button::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_text","text"),&Button::set_text);
@@ -227,6 +226,10 @@ void Button::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_text_align"),&Button::get_text_align);
ObjectTypeDB::bind_method(_MD("is_flat"),&Button::is_flat);
+ BIND_CONSTANT( ALIGN_LEFT );
+ BIND_CONSTANT( ALIGN_CENTER );
+ BIND_CONSTANT( ALIGN_RIGHT );
+
ADD_PROPERTYNZ( PropertyInfo( Variant::STRING, "text", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL ), _SCS("set_text"),_SCS("get_text") );
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), _SCS("set_button_icon"),_SCS("get_button_icon") );
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flat" ), _SCS("set_flat"),_SCS("is_flat") );
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 8a17a164a0..c39237c9af 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -54,7 +54,6 @@ private:
TextAlign align;
-
protected:
virtual Size2 get_minimum_size() const;
@@ -62,6 +61,8 @@ protected:
static void _bind_methods();
public:
//
+
+
void set_text(const String& p_text);
String get_text() const;
@@ -77,6 +78,7 @@ public:
void set_text_align(TextAlign p_align);
TextAlign get_text_align() const;
+
Button(const String& p_text=String());
~Button();
diff --git a/scene/gui/button_array.cpp b/scene/gui/button_array.cpp
index de77b83403..be48296110 100644
--- a/scene/gui/button_array.cpp
+++ b/scene/gui/button_array.cpp
@@ -362,10 +362,10 @@ ButtonArray::Align ButtonArray::get_align() const {
}
-void ButtonArray::add_button(const String& p_button) {
+void ButtonArray::add_button(const String& p_text) {
Button button;
- button.text=p_button;
+ button.text=p_text;
buttons.push_back(button);
update();
@@ -375,10 +375,10 @@ void ButtonArray::add_button(const String& p_button) {
minimum_size_changed();
}
-void ButtonArray::add_icon_button(const Ref<Texture>& p_icon,const String& p_button) {
+void ButtonArray::add_icon_button(const Ref<Texture>& p_icon,const String& p_text) {
Button button;
- button.text=p_button;
+ button.text=p_text;
button.icon=p_icon;
buttons.push_back(button);
if (selected==-1)
@@ -396,6 +396,7 @@ void ButtonArray::set_button_text(int p_button, const String& p_text) {
minimum_size_changed();
}
+
void ButtonArray::set_button_icon(int p_button, const Ref<Texture>& p_icon) {
ERR_FAIL_INDEX(p_button,buttons.size());
@@ -403,11 +404,13 @@ void ButtonArray::set_button_icon(int p_button, const Ref<Texture>& p_icon) {
update();
minimum_size_changed();
}
+
String ButtonArray::get_button_text(int p_button) const {
ERR_FAIL_INDEX_V(p_button,buttons.size(),"");
return buttons[p_button].text;
}
+
Ref<Texture> ButtonArray::get_button_icon(int p_button) const {
ERR_FAIL_INDEX_V(p_button,buttons.size(),Ref<Texture>());
@@ -470,16 +473,16 @@ void ButtonArray::get_translatable_strings(List<String> *p_strings) const {
void ButtonArray::_bind_methods() {
ObjectTypeDB::bind_method(_MD("add_button","text"),&ButtonArray::add_button);
- ObjectTypeDB::bind_method(_MD("add_icon_button","icon","text"),&ButtonArray::add_icon_button,DEFVAL(""));
- ObjectTypeDB::bind_method(_MD("set_button_text","button","text"),&ButtonArray::set_button_text);
- ObjectTypeDB::bind_method(_MD("set_button_icon","button","icon"),&ButtonArray::set_button_icon);
- ObjectTypeDB::bind_method(_MD("get_button_text","button"),&ButtonArray::get_button_text);
- ObjectTypeDB::bind_method(_MD("get_button_icon","button"),&ButtonArray::get_button_icon);
+ ObjectTypeDB::bind_method(_MD("add_icon_button","icon:Texture","text"),&ButtonArray::add_icon_button,DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("set_button_text","button_idx","text"),&ButtonArray::set_button_text);
+ ObjectTypeDB::bind_method(_MD("set_button_icon","button_idx","icon:Texture"),&ButtonArray::set_button_icon);
+ ObjectTypeDB::bind_method(_MD("get_button_text","button_idx"),&ButtonArray::get_button_text);
+ ObjectTypeDB::bind_method(_MD("get_button_icon:Texture","button_idx"),&ButtonArray::get_button_icon);
ObjectTypeDB::bind_method(_MD("get_button_count"),&ButtonArray::get_button_count);
ObjectTypeDB::bind_method(_MD("get_selected"),&ButtonArray::get_selected);
ObjectTypeDB::bind_method(_MD("get_hovered"),&ButtonArray::get_hovered);
- ObjectTypeDB::bind_method(_MD("set_selected","button"),&ButtonArray::set_selected);
- ObjectTypeDB::bind_method(_MD("erase_button","button"),&ButtonArray::erase_button);
+ ObjectTypeDB::bind_method(_MD("set_selected","button_idx"),&ButtonArray::set_selected);
+ ObjectTypeDB::bind_method(_MD("erase_button","button_idx"),&ButtonArray::erase_button);
ObjectTypeDB::bind_method(_MD("clear"),&ButtonArray::clear);
ObjectTypeDB::bind_method(_MD("_input_event"),&ButtonArray::_input_event);
@@ -490,7 +493,7 @@ void ButtonArray::_bind_methods() {
BIND_CONSTANT( ALIGN_FILL );
BIND_CONSTANT( ALIGN_EXPAND_FILL );
- ADD_SIGNAL( MethodInfo("button_selected",PropertyInfo(Variant::INT,"button")));
+ ADD_SIGNAL( MethodInfo("button_selected",PropertyInfo(Variant::INT,"button_idx")));
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index d7befd6e11..bd56369746 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -107,8 +107,32 @@ bool Control::_set(const StringName& p_name, const Variant& p_value) {
String name= p_name;
- if (!name.begins_with("custom"))
- return false;
+ if (!name.begins_with("custom")) {
+ if (name.begins_with("margin/")) {
+ String dname = name.get_slicec('/', 1);
+ if (dname == "left") {
+ set_margin(MARGIN_LEFT, p_value);
+ return true;
+ }
+ else if (dname == "top") {
+ set_margin(MARGIN_TOP, p_value);
+ return true;
+ }
+ else if (dname == "right") {
+ set_margin(MARGIN_RIGHT, p_value);
+ return true;
+ }
+ else if (dname == "bottom") {
+ set_margin(MARGIN_BOTTOM, p_value);
+ return true;
+ }
+ else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
if (p_value.get_type()==Variant::NIL) {
@@ -200,7 +224,30 @@ bool Control::_get(const StringName& p_name,Variant &r_ret) const {
String sname=p_name;
if (!sname.begins_with("custom"))
- return false;
+ if (sname.begins_with("margin/")) {
+ String dname = sname.get_slicec('/', 1);
+ if (dname == "left") {
+ r_ret = get_margin(MARGIN_LEFT);
+ return true;
+ }
+ else if (dname == "top") {
+ r_ret = get_margin(MARGIN_TOP);
+ return true;
+ }
+ else if (dname == "right") {
+ r_ret = get_margin(MARGIN_RIGHT);
+ return true;
+ }
+ else if (dname == "bottom") {
+ r_ret = get_margin(MARGIN_BOTTOM);
+ return true;
+ }
+ else {
+ return false;
+ }
+ } else {
+ return false;
+ }
if (sname.begins_with("custom_icons/")) {
String name = sname.get_slicec('/',1);
@@ -236,6 +283,36 @@ bool Control::_get(const StringName& p_name,Variant &r_ret) const {
}
void Control::_get_property_list( List<PropertyInfo> *p_list) const {
+ {
+ if (get_anchor(MARGIN_LEFT) == ANCHOR_RATIO) {
+ p_list->push_back(PropertyInfo(Variant::REAL, "margin/left", PROPERTY_HINT_RANGE, "-4096,4096,0.001"));
+ }
+ else {
+ p_list->push_back(PropertyInfo(Variant::INT, "margin/left", PROPERTY_HINT_RANGE, "-4096,4096"));
+ }
+
+ if (get_anchor(MARGIN_TOP) == ANCHOR_RATIO) {
+ p_list->push_back(PropertyInfo(Variant::REAL, "margin/top", PROPERTY_HINT_RANGE, "-4096,4096,0.001"));
+ }
+ else {
+ p_list->push_back(PropertyInfo(Variant::INT, "margin/top", PROPERTY_HINT_RANGE, "-4096,4096"));
+ }
+
+ if (get_anchor(MARGIN_RIGHT) == ANCHOR_RATIO) {
+ p_list->push_back(PropertyInfo(Variant::REAL, "margin/right", PROPERTY_HINT_RANGE, "-4096,4096,0.001"));
+ }
+ else {
+ p_list->push_back(PropertyInfo(Variant::INT, "margin/right", PROPERTY_HINT_RANGE, "-4096,4096"));
+ }
+
+ if (get_anchor(MARGIN_BOTTOM) == ANCHOR_RATIO) {
+ p_list->push_back(PropertyInfo(Variant::REAL, "margin/bottom", PROPERTY_HINT_RANGE, "-4096,4096,0.001"));
+ }
+ else {
+ p_list->push_back(PropertyInfo(Variant::INT, "margin/bottom", PROPERTY_HINT_RANGE, "-4096,4096"));
+ }
+ }
+
Ref<Theme> theme;
if (data.theme.is_valid()) {
@@ -525,10 +602,13 @@ void Control::_notification(int p_notification) {
if (!is_visible()) {
+ if(get_viewport() != NULL)
+ get_viewport()->_gui_hid_control(this);
- get_viewport()->_gui_hid_control(this);
- _modal_stack_remove();
- minimum_size_changed();
+ if(is_inside_tree()) {
+ _modal_stack_remove();
+ minimum_size_changed();
+ }
//remove key focus
//remove modalness
@@ -572,8 +652,24 @@ bool Control::has_point(const Point2& p_point) const {
return Rect2( Point2(), get_size() ).has_point(p_point);
}
+void Control::set_drag_forwarding(Control* p_target) {
+
+ if (p_target)
+ data.drag_owner=p_target->get_instance_ID();
+ else
+ data.drag_owner=0;
+}
+
Variant Control::get_drag_data(const Point2& p_point) {
+ if (data.drag_owner) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ Control *c = obj->cast_to<Control>();
+ return c->call("get_drag_data_fw",p_point,this);
+ }
+ }
+
if (get_script_instance()) {
Variant v=p_point;
const Variant *p=&v;
@@ -589,6 +685,14 @@ Variant Control::get_drag_data(const Point2& p_point) {
bool Control::can_drop_data(const Point2& p_point,const Variant& p_data) const {
+ if (data.drag_owner) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ Control *c = obj->cast_to<Control>();
+ return c->call("can_drop_data_fw",p_point,p_data,this);
+ }
+ }
+
if (get_script_instance()) {
Variant v=p_point;
const Variant *p[2]={&v,&p_data};
@@ -603,6 +707,15 @@ bool Control::can_drop_data(const Point2& p_point,const Variant& p_data) const {
}
void Control::drop_data(const Point2& p_point,const Variant& p_data){
+ if (data.drag_owner) {
+ Object *obj = ObjectDB::get_instance(data.drag_owner);
+ if (obj) {
+ Control *c = obj->cast_to<Control>();
+ c->call("drop_data_fw",p_point,p_data,this);
+ return;
+ }
+ }
+
if (get_script_instance()) {
Variant v=p_point;
const Variant *p[2]={&v,&p_data};
@@ -631,7 +744,6 @@ void Control::set_drag_preview(Control *p_control) {
-
bool Control::is_window_modal_on_top() const {
if (!is_inside_tree())
@@ -658,7 +770,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()) {
+ if (p_type==StringName() || p_type=="") {
const Ref<Texture>* tex = data.icon_override.getptr(p_name);
if (tex)
@@ -688,7 +800,7 @@ Ref<Texture> Control::get_icon(const StringName& p_name,const StringName& p_type
}
Ref<Shader> Control::get_shader(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
+ if (p_type==StringName() || p_type=="") {
const Ref<Shader>* sdr = data.shader_override.getptr(p_name);
if (sdr)
@@ -718,7 +830,7 @@ Ref<Shader> Control::get_shader(const StringName& p_name,const StringName& p_typ
Ref<StyleBox> Control::get_stylebox(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
+ if (p_type==StringName() || p_type=="") {
const Ref<StyleBox>* style = data.style_override.getptr(p_name);
if (style)
return *style;
@@ -746,7 +858,7 @@ Ref<StyleBox> Control::get_stylebox(const StringName& p_name,const StringName& p
}
Ref<Font> Control::get_font(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
+ if (p_type==StringName() || p_type=="") {
const Ref<Font>* font = data.font_override.getptr(p_name);
if (font)
return *font;
@@ -777,7 +889,7 @@ Ref<Font> Control::get_font(const StringName& p_name,const StringName& p_type) c
}
Color Control::get_color(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
+ if (p_type==StringName() || p_type=="") {
const Color* color = data.color_override.getptr(p_name);
if (color)
return *color;
@@ -806,7 +918,7 @@ Color Control::get_color(const StringName& p_name,const StringName& p_type) cons
int Control::get_constant(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
+ if (p_type==StringName() || p_type=="") {
const int* constant = data.constant_override.getptr(p_name);
if (constant)
return *constant;
@@ -834,12 +946,64 @@ int Control::get_constant(const StringName& p_name,const StringName& p_type) con
}
+bool Control::has_icon_override(const StringName& p_name) const {
+
+ const Ref<Texture>* tex = data.icon_override.getptr(p_name);
+ if (tex)
+ return true;
+ else
+ return false;
+}
+
+bool Control::has_shader_override(const StringName &p_name) const {
+
+ const Ref<Shader>* sdr = data.shader_override.getptr(p_name);
+ if (sdr)
+ return true;
+ else
+ return false;
+}
+
+bool Control::has_stylebox_override(const StringName& p_name) const {
+
+ const Ref<StyleBox>* style = data.style_override.getptr(p_name);
+ if (style)
+ return true;
+ else
+ return false;
+}
+
+bool Control::has_font_override(const StringName& p_name) const {
+
+ const Ref<Font>* font = data.font_override.getptr(p_name);
+ if (font)
+ return true;
+ else
+ return false;
+}
+
+bool Control::has_color_override(const StringName& p_name) const {
+
+ const Color* color = data.color_override.getptr(p_name);
+ if (color)
+ return true;
+ else
+ return false;
+}
+
+bool Control::has_constant_override(const StringName& p_name) const {
+
+ const int* constant = data.constant_override.getptr(p_name);
+ if (constant)
+ return true;
+ else
+ return false;
+}
bool Control::has_icon(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
- const Ref<Texture>* tex = data.icon_override.getptr(p_name);
- if (tex)
+ if (p_type==StringName() || p_type=="") {
+ if (has_icon_override(p_name) == true)
return true;
}
@@ -865,11 +1029,10 @@ 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()) {
- const Ref<Shader>* sdr = data.shader_override.getptr(p_name);
- if (sdr)
+bool Control::has_shader(const StringName &p_name, const StringName &p_type) const {
+
+ if (p_type==StringName() || p_type=="") {
+ if (has_shader_override(p_name)==true)
return true;
}
@@ -896,10 +1059,8 @@ 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()) {
- const Ref<StyleBox>* style = data.style_override.getptr(p_name);
-
- if (style)
+ if (p_type==StringName() || p_type=="") {
+ if (has_stylebox_override(p_name)==true)
return true;
}
@@ -926,9 +1087,8 @@ bool Control::has_stylebox(const StringName& p_name,const StringName& p_type) co
}
bool Control::has_font(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
- const Ref<Font>* font = data.font_override.getptr(p_name);
- if (font)
+ if (p_type==StringName() || p_type=="") {
+ if (has_font_override(p_name)==true)
return true;
}
@@ -954,11 +1114,11 @@ bool Control::has_font(const StringName& p_name,const StringName& p_type) const
return Theme::get_default()->has_font( p_name, type );
}
-bool Control::has_color(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
- const Color* color = data.color_override.getptr(p_name);
- if (color)
+bool Control::has_color(const StringName& p_name, const StringName& p_type) const {
+
+ if (p_type==StringName() || p_type=="") {
+ if (has_color_override(p_name)==true)
return true;
}
@@ -986,10 +1146,8 @@ bool Control::has_color(const StringName& p_name,const StringName& p_type) const
bool Control::has_constant(const StringName& p_name,const StringName& p_type) const {
- if (p_type==StringName()) {
-
- const int* constant = data.constant_override.getptr(p_name);
- if (constant)
+ if (p_type==StringName() || p_type=="") {
+ if (has_constant_override(p_name) == true)
return true;
}
@@ -1175,14 +1333,13 @@ void Control::set_anchor(Margin p_margin,AnchorType p_anchor, bool p_keep_margin
void Control::_set_anchor(Margin p_margin,AnchorType p_anchor) {
#ifdef TOOLS_ENABLED
- SceneTree *st=OS::get_singleton()->get_main_loop()->cast_to<SceneTree>();
- if (st && st->is_editor_hint()) {
+ if (is_inside_tree() && get_tree()->is_editor_hint()) {
set_anchor(p_margin, p_anchor, EDITOR_DEF("2d_editor/keep_margins_when_changing_anchors", false));
} else {
- set_anchor(p_margin, p_anchor);
+ set_anchor(p_margin, p_anchor, false);
}
#else
- set_anchor(p_margin, p_anchor);
+ set_anchor(p_margin, p_anchor, false);
#endif
}
@@ -1699,6 +1856,7 @@ void Control::_propagate_theme_changed(Control *p_owner) {
void Control::set_theme(const Ref<Theme>& p_theme) {
+
data.theme=p_theme;
if (!p_theme.is_null()) {
@@ -2045,17 +2203,9 @@ bool Control::is_text_field() const {
}
-void Control::_set_rotation_deg(float p_rot) {
- set_rotation(Math::deg2rad(p_rot));
-}
-
-float Control::_get_rotation_deg() const {
- return Math::rad2deg(get_rotation());
-}
-
-void Control::set_rotation(float p_rotation) {
+void Control::set_rotation(float p_radians) {
- data.rotation=p_rotation;
+ data.rotation=p_radians;
update();
_notify_transform();
}
@@ -2065,6 +2215,25 @@ float Control::get_rotation() const{
return data.rotation;
}
+void Control::set_rotation_deg(float p_degrees) {
+ set_rotation(Math::deg2rad(p_degrees));
+}
+
+float Control::get_rotation_deg() const {
+ return Math::rad2deg(get_rotation());
+}
+
+// Kept for compatibility after rename to {s,g}et_rotation_deg.
+// Could be removed after a couple releases.
+void Control::_set_rotation_deg(float p_degrees) {
+ WARN_PRINT("Deprecated method Control._set_rotation_deg(): This method was renamed to set_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted.");
+ set_rotation_deg(p_degrees);
+}
+float Control::_get_rotation_deg() const {
+ WARN_PRINT("Deprecated method Control._get_rotation_deg(): This method was renamed to get_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted.");
+ return get_rotation_deg();
+}
+
void Control::set_scale(const Vector2& p_scale){
data.scale=p_scale;
@@ -2120,8 +2289,10 @@ void Control::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_size","size"),&Control::set_size);
ObjectTypeDB::bind_method(_MD("set_custom_minimum_size","size"),&Control::set_custom_minimum_size);
ObjectTypeDB::bind_method(_MD("set_global_pos","pos"),&Control::set_global_pos);
- ObjectTypeDB::bind_method(_MD("set_rotation","rotation"),&Control::set_rotation);
- ObjectTypeDB::bind_method(_MD("_set_rotation_deg","rotation"),&Control::_set_rotation_deg);
+ ObjectTypeDB::bind_method(_MD("set_rotation","radians"),&Control::set_rotation);
+ ObjectTypeDB::bind_method(_MD("set_rotation_deg","degrees"),&Control::set_rotation_deg);
+ // TODO: Obsolete this method (old name) properly (GH-4397)
+ ObjectTypeDB::bind_method(_MD("_set_rotation_deg","degrees"),&Control::_set_rotation_deg);
ObjectTypeDB::bind_method(_MD("set_scale","scale"),&Control::set_scale);
ObjectTypeDB::bind_method(_MD("get_margin","margin"),&Control::get_margin);
ObjectTypeDB::bind_method(_MD("get_begin"),&Control::get_begin);
@@ -2129,16 +2300,19 @@ void Control::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_pos"),&Control::get_pos);
ObjectTypeDB::bind_method(_MD("get_size"),&Control::get_size);
ObjectTypeDB::bind_method(_MD("get_rotation"),&Control::get_rotation);
+ ObjectTypeDB::bind_method(_MD("get_rotation_deg"),&Control::get_rotation_deg);
+ // TODO: Obsolete this method (old name) properly (GH-4397)
+ ObjectTypeDB::bind_method(_MD("_get_rotation_deg"),&Control::_get_rotation_deg);
ObjectTypeDB::bind_method(_MD("get_scale"),&Control::get_scale);
ObjectTypeDB::bind_method(_MD("get_custom_minimum_size"),&Control::get_custom_minimum_size);
ObjectTypeDB::bind_method(_MD("get_parent_area_size"),&Control::get_size);
ObjectTypeDB::bind_method(_MD("get_global_pos"),&Control::get_global_pos);
ObjectTypeDB::bind_method(_MD("get_rect"),&Control::get_rect);
- ObjectTypeDB::bind_method(_MD("_get_rotation_deg"),&Control::_get_rotation_deg);
ObjectTypeDB::bind_method(_MD("get_global_rect"),&Control::get_global_rect);
ObjectTypeDB::bind_method(_MD("set_area_as_parent_rect","margin"),&Control::set_area_as_parent_rect,DEFVAL(0));
ObjectTypeDB::bind_method(_MD("show_modal","exclusive"),&Control::show_modal,DEFVAL(false));
ObjectTypeDB::bind_method(_MD("set_focus_mode","mode"),&Control::set_focus_mode);
+ ObjectTypeDB::bind_method(_MD("get_focus_mode"),&Control::get_focus_mode);
ObjectTypeDB::bind_method(_MD("has_focus"),&Control::has_focus);
ObjectTypeDB::bind_method(_MD("grab_focus"),&Control::grab_focus);
ObjectTypeDB::bind_method(_MD("release_focus"),&Control::release_focus);
@@ -2169,6 +2343,17 @@ void Control::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_color","name","type"),&Control::get_color,DEFVAL(""));
ObjectTypeDB::bind_method(_MD("get_constant","name","type"),&Control::get_constant,DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("has_icon_override", "name"), &Control::has_icon_override);
+ ObjectTypeDB::bind_method(_MD("has_stylebox_override", "name"), &Control::has_stylebox_override);
+ ObjectTypeDB::bind_method(_MD("has_font_override", "name"), &Control::has_font_override);
+ ObjectTypeDB::bind_method(_MD("has_color_override", "name"), &Control::has_color_override);
+ ObjectTypeDB::bind_method(_MD("has_constant_override", "name"), &Control::has_constant_override);
+
+ ObjectTypeDB::bind_method(_MD("has_icon", "name", "type"), &Control::has_icon, DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("has_stylebox", "name", "type"), &Control::has_stylebox, DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("has_font", "name", "type"), &Control::has_font, DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("has_color", "name", "type"), &Control::has_color, DEFVAL(""));
+ ObjectTypeDB::bind_method(_MD("has_constant", "name", "type"), &Control::has_constant, DEFVAL(""));
ObjectTypeDB::bind_method(_MD("get_parent_control:Control"),&Control::get_parent_control);
@@ -2193,10 +2378,12 @@ void Control::_bind_methods() {
ObjectTypeDB::bind_method(_MD("grab_click_focus"),&Control::grab_click_focus);
+ ObjectTypeDB::bind_method(_MD("set_drag_forwarding","target:Control"),&Control::set_drag_forwarding);
ObjectTypeDB::bind_method(_MD("set_drag_preview","control:Control"),&Control::set_drag_preview);
ObjectTypeDB::bind_method(_MD("warp_mouse","to_pos"),&Control::warp_mouse);
+ ObjectTypeDB::bind_method(_MD("minimum_size_changed"), &Control::minimum_size_changed);
BIND_VMETHOD(MethodInfo("_input_event",PropertyInfo(Variant::INPUT_EVENT,"event")));
BIND_VMETHOD(MethodInfo(Variant::VECTOR2,"get_minimum_size"));
@@ -2209,15 +2396,10 @@ void Control::_bind_methods() {
ADD_PROPERTYINZ( PropertyInfo(Variant::INT,"anchor/right", PROPERTY_HINT_ENUM, "Begin,End,Ratio,Center"), _SCS("_set_anchor"),_SCS("get_anchor"), MARGIN_RIGHT );
ADD_PROPERTYINZ( PropertyInfo(Variant::INT,"anchor/bottom", PROPERTY_HINT_ENUM, "Begin,End,Ratio,Center"), _SCS("_set_anchor"),_SCS("get_anchor"), MARGIN_BOTTOM );
- ADD_PROPERTYINZ( PropertyInfo(Variant::INT,"margin/left", PROPERTY_HINT_RANGE, "-4096,4096"), _SCS("set_margin"),_SCS("get_margin"), MARGIN_LEFT );
- ADD_PROPERTYINZ( PropertyInfo(Variant::INT,"margin/top", PROPERTY_HINT_RANGE, "-4096,4096"), _SCS("set_margin"),_SCS("get_margin"), MARGIN_TOP );
- ADD_PROPERTYINZ( PropertyInfo(Variant::INT,"margin/right", PROPERTY_HINT_RANGE, "-4096,4096"), _SCS("set_margin"),_SCS("get_margin"), MARGIN_RIGHT );
- ADD_PROPERTYINZ( PropertyInfo(Variant::INT,"margin/bottom", PROPERTY_HINT_RANGE, "-4096,4096"), _SCS("set_margin"),_SCS("get_margin"), MARGIN_BOTTOM );
-
ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"rect/pos", PROPERTY_HINT_NONE, "",PROPERTY_USAGE_EDITOR), _SCS("set_pos"),_SCS("get_pos") );
ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"rect/size", PROPERTY_HINT_NONE, "",PROPERTY_USAGE_EDITOR), _SCS("set_size"),_SCS("get_size") );
ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"rect/min_size"), _SCS("set_custom_minimum_size"),_SCS("get_custom_minimum_size") );
- ADD_PROPERTYNZ( PropertyInfo(Variant::REAL,"rect/rotation",PROPERTY_HINT_RANGE,"-1080,1080,0.01"), _SCS("_set_rotation_deg"),_SCS("_get_rotation_deg") );
+ ADD_PROPERTYNZ( PropertyInfo(Variant::REAL,"rect/rotation",PROPERTY_HINT_RANGE,"-1080,1080,0.01"), _SCS("set_rotation_deg"),_SCS("get_rotation_deg") );
ADD_PROPERTYNO( PropertyInfo(Variant::VECTOR2,"rect/scale"), _SCS("set_scale"),_SCS("get_scale") );
ADD_PROPERTYNZ( PropertyInfo(Variant::STRING,"hint/tooltip", PROPERTY_HINT_MULTILINE_TEXT), _SCS("set_tooltip"),_SCS("_get_tooltip") );
ADD_PROPERTYINZ( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/left" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_LEFT );
@@ -2235,7 +2417,7 @@ void Control::_bind_methods() {
BIND_CONSTANT( ANCHOR_BEGIN );
BIND_CONSTANT( ANCHOR_END );
BIND_CONSTANT( ANCHOR_RATIO );
- BIND_CONSTANT( ANCHOR_CENTER );
+ BIND_CONSTANT( ANCHOR_CENTER );
BIND_CONSTANT( FOCUS_NONE );
BIND_CONSTANT( FOCUS_CLICK );
BIND_CONSTANT( FOCUS_ALL );
@@ -2305,6 +2487,7 @@ Control::Control() {
data.rotation=0;
data.parent_canvas_item=NULL;
data.scale=Vector2(1,1);
+ data.drag_owner=0;
for (int i=0;i<4;i++) {
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 1e2db7a575..59704ae29b 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -35,7 +35,7 @@
#include "scene/2d/canvas_item.h"
#include "math_2d.h"
#include "rid.h"
-
+#include "scene/gui/input_action.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -129,6 +129,7 @@ private:
bool stop_mouse;
Control *parent;
+ ObjectID drag_owner;
bool modal;
bool modal_exclusive;
Ref<Theme> theme;
@@ -179,7 +180,8 @@ private:
void _size_changed();
String _get_tooltip() const;
- void _set_rotation_deg(float p_rot);
+ // Deprecated, should be removed in a future version.
+ void _set_rotation_deg(float p_degrees);
float _get_rotation_deg() const;
friend class Viewport;
@@ -229,6 +231,7 @@ public:
virtual Size2 get_combined_minimum_size() const;
virtual bool has_point(const Point2& p_point) const;
virtual bool clips_input() const;
+ virtual void set_drag_forwarding(Control* p_target);
virtual Variant get_drag_data(const Point2& p_point);
virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const;
virtual void drop_data(const Point2& p_point,const Variant& p_data);
@@ -273,8 +276,10 @@ public:
Rect2 get_global_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
- void set_rotation(float p_rotation);
+ void set_rotation(float p_radians);
+ void set_rotation_deg(float p_degrees);
float get_rotation() const;
+ float get_rotation_deg() const;
void set_scale(const Vector2& p_scale);
Vector2 get_scale() const;
@@ -336,6 +341,13 @@ public:
Color get_color(const StringName& p_name,const StringName& p_type=StringName()) const;
int get_constant(const StringName& p_name,const StringName& p_type=StringName()) const;
+ bool has_icon_override(const StringName& p_name) const;
+ bool has_shader_override(const StringName& p_name) const;
+ bool has_stylebox_override(const StringName& p_name) const;
+ bool has_font_override(const StringName& p_name) const;
+ bool has_color_override(const StringName& p_name) const;
+ bool has_constant_override(const StringName& p_name) const;
+
bool has_icon(const StringName& p_name,const StringName& p_type=StringName()) const;
bool has_shader(const StringName& p_name,const StringName& p_type=StringName()) const;
bool has_stylebox(const StringName& p_name,const StringName& p_type=StringName()) const;
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index d00dacd256..6342391383 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -118,6 +118,16 @@ void WindowDialog::set_title(const String& p_title) {
update();
}
+Size2 WindowDialog::get_minimum_size() const {
+
+ Ref<Font> font = get_font("title_font","WindowDialog");
+ int msx=close_button->get_combined_minimum_size().x;
+ msx+=font->get_string_size(title).x;
+
+ return Size2(msx,1);
+}
+
+
String WindowDialog::get_title() const {
return title;
@@ -192,11 +202,9 @@ void AcceptDialog::_notification(int p_what) {
if (p_what==NOTIFICATION_MODAL_CLOSE) {
cancel_pressed();
- } if (p_what==NOTIFICATION_DRAW) {
-
-
-
+ } if (p_what==NOTIFICATION_RESIZED) {
+ _update_child_rect();
}
}
@@ -244,12 +252,69 @@ void AcceptDialog::register_text_enter(Node *p_line_edit) {
p_line_edit->connect("text_entered", this,"_builtin_text_entered");
}
+void AcceptDialog::_update_child_rect() {
+
+ int margin = get_constant("margin","Dialogs");
+ Size2 size = get_size();
+ Size2 hminsize = hbc->get_combined_minimum_size();
+
+ Vector2 cpos(margin,margin);
+ Vector2 csize(size.x-margin*2,size.y-margin*3-hminsize.y);
+ label->set_pos(cpos);
+ label->set_size(csize);
+
+ if (child) {
+
+ child->set_pos(cpos);
+ child->set_size(csize);
+ }
+
+ cpos.y+=csize.y+margin;
+ csize.y=hminsize.y;
+
+ hbc->set_pos(cpos);
+ hbc->set_size(csize);
+
+}
+
+Size2 AcceptDialog::get_minimum_size() const {
+
+ int margin = get_constant("margin","Dialogs");
+ Size2 minsize = label->get_combined_minimum_size();
+ if (child) {
+
+ Size2 cminsize = child->get_combined_minimum_size();
+ minsize.x=MAX(cminsize.x,minsize.x);
+ minsize.y=MAX(cminsize.y,minsize.y);
+ }
+
+ Size2 hminsize = hbc->get_combined_minimum_size();
+ minsize.x = MAX(hminsize.x,minsize.x);
+ minsize.y+=hminsize.y;
+ minsize.x+=margin*2;
+ minsize.y+=margin*3; //one as separation between hbc and child
+
+ Size2 wmsize = WindowDialog::get_minimum_size();
+ minsize.x=MAX(wmsize.x,minsize.x);
+ return minsize;
+}
+
+
void AcceptDialog::set_child_rect(Control *p_child) {
ERR_FAIL_COND(p_child->get_parent()!=this);
- p_child->set_area_as_parent_rect(get_constant("margin","Dialogs"));
- p_child->set_margin(MARGIN_BOTTOM, get_constant("button_margin","Dialogs")+10);
+ //p_child->set_area_as_parent_rect(get_constant("margin","Dialogs"));
+ child=p_child;
+ minimum_size_changed();
+ _update_child_rect();
+}
+
+void AcceptDialog::remove_child_notify(Node *p_child) {
+
+ if (p_child==child) {
+ child=NULL;
+ }
}
void AcceptDialog::_custom_action(const String& p_action) {
@@ -284,8 +349,8 @@ Button* AcceptDialog::add_cancel(const String &p_cancel) {
String c = p_cancel;
if (p_cancel=="")
- c="Cancel";
- Button *b = swap_ok_cancel ? add_button("Cancel",true) : add_button("Cancel");
+ c=RTR("Cancel");
+ Button *b = swap_ok_cancel ? add_button(c,true) : add_button(c);
b->connect("pressed",this,"_closed");
return b;
}
@@ -341,7 +406,7 @@ AcceptDialog::AcceptDialog() {
hbc->add_spacer();
ok = memnew( Button );
- ok->set_text("OK");
+ ok->set_text(RTR("OK"));
hbc->add_child(ok);
hbc->add_spacer();
//add_child(ok);
@@ -351,7 +416,9 @@ AcceptDialog::AcceptDialog() {
set_as_toplevel(true);
hide_on_ok=true;
- set_title("Alert!");
+ set_title(RTR("Alert!"));
+
+ child=NULL;
}
@@ -372,6 +439,6 @@ Button *ConfirmationDialog::get_cancel() {
ConfirmationDialog::ConfirmationDialog() {
- set_title("Please Confirm...");
+ set_title(RTR("Please Confirm..."));
cancel = add_cancel();
}
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index f256c49aee..d00bb41ff6 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -64,6 +64,8 @@ public:
void set_title(const String& p_title);
String get_title() const;
+ Size2 get_minimum_size() const;
+
WindowDialog();
~WindowDialog();
@@ -89,6 +91,7 @@ class AcceptDialog : public WindowDialog {
OBJ_TYPE(AcceptDialog,WindowDialog);
+ Control *child;
HBoxContainer *hbc;
Label *label;
Button *ok;
@@ -100,10 +103,12 @@ class AcceptDialog : public WindowDialog {
void _ok_pressed();
void _close_pressed();
void _builtin_text_entered(const String& p_text);
+ void _update_child_rect();
static bool swap_ok_cancel;
+ virtual void remove_child_notify(Node *p_child);
protected:
@@ -116,6 +121,8 @@ protected:
virtual void custom_action(const String&) {}
public:
+ Size2 get_minimum_size() const;
+
Label *get_label() { return label; }
static void set_swap_ok_cancel(bool p_swap);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 64fdfdfefe..705ce55d42 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -613,7 +613,6 @@ FileDialog::Access FileDialog::get_access() const{
void FileDialog::_make_dir_confirm() {
-
Error err = dir_access->make_dir( makedirname->get_text() );
if (err==OK) {
dir_access->change_dir(makedirname->get_text());
@@ -623,6 +622,7 @@ void FileDialog::_make_dir_confirm() {
} else {
mkdirerr->popup_centered_minsize(Size2(250,50));
}
+ makedirname->set_text(""); // reset label
}
diff --git a/scene/gui/input_action.cpp b/scene/gui/input_action.cpp
new file mode 100644
index 0000000000..c4e7a75298
--- /dev/null
+++ b/scene/gui/input_action.cpp
@@ -0,0 +1,125 @@
+#include "input_action.h"
+#include "os/keyboard.h"
+
+void ShortCut::set_shortcut(const InputEvent& p_shortcut){
+
+ shortcut=p_shortcut;
+ emit_changed();
+}
+
+InputEvent ShortCut::get_shortcut() const{
+
+ return shortcut;
+}
+
+bool ShortCut::is_shortcut(const InputEvent& p_event) const {
+
+ bool same=false;
+
+
+ switch(p_event.type) {
+
+ case InputEvent::KEY: {
+
+ same=(shortcut.key.scancode==p_event.key.scancode && shortcut.key.mod == p_event.key.mod);
+
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+
+ same=(shortcut.joy_button.button_index==p_event.joy_button.button_index);
+
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+
+ same=(shortcut.mouse_button.button_index==p_event.mouse_button.button_index);
+
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ same=(shortcut.joy_motion.axis==p_event.joy_motion.axis && (shortcut.joy_motion.axis_value < 0) == (p_event.joy_motion.axis_value < 0));
+
+ } break;
+ default: {};
+ }
+
+ return same;
+}
+
+String ShortCut::get_as_text() const {
+
+ switch(shortcut.type) {
+
+ case InputEvent::NONE: {
+
+ return "None";
+ } break;
+ case InputEvent::KEY: {
+
+ String str;
+ if (shortcut.key.mod.shift)
+ str+=RTR("Shift+");
+ if (shortcut.key.mod.alt)
+ str+=RTR("Alt+");
+ if (shortcut.key.mod.control)
+ str+=RTR("Ctrl+");
+ if (shortcut.key.mod.meta)
+ str+=RTR("Meta+");
+
+ str+=keycode_get_string(shortcut.key.scancode).capitalize();
+
+ return str;
+ } break;
+ case InputEvent::JOYSTICK_BUTTON: {
+
+ String str = RTR("Device")+" "+itos(shortcut.device)+", "+RTR("Button")+" "+itos(shortcut.joy_button.button_index);
+ str+=".";
+
+ return str;
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+
+ String str = RTR("Device")+" "+itos(shortcut.device)+", ";
+ switch (shortcut.mouse_button.button_index) {
+ case BUTTON_LEFT: str+=RTR("Left Button."); break;
+ case BUTTON_RIGHT: str+=RTR("Right Button."); break;
+ case BUTTON_MIDDLE: str+=RTR("Middle Button."); break;
+ case BUTTON_WHEEL_UP: str+=RTR("Wheel Up."); break;
+ case BUTTON_WHEEL_DOWN: str+=RTR("Wheel Down."); break;
+ default: str+=RTR("Button")+" "+itos(shortcut.mouse_button.button_index)+".";
+ }
+
+ return str;
+ } break;
+ case InputEvent::JOYSTICK_MOTION: {
+
+ int ax = shortcut.joy_motion.axis;
+ String str = RTR("Device")+" "+itos(shortcut.device)+", "+RTR("Axis")+" "+itos(ax)+".";
+
+ return str;
+ } break;
+ }
+
+ return "";
+}
+
+bool ShortCut::is_valid() const {
+
+ return shortcut.type!=InputEvent::NONE;
+}
+
+void ShortCut::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_shortcut","event"),&ShortCut::set_shortcut);
+ ObjectTypeDB::bind_method(_MD("get_shortcut"),&ShortCut::get_shortcut);
+
+ ObjectTypeDB::bind_method(_MD("is_valid"),&ShortCut::is_valid);
+
+ ObjectTypeDB::bind_method(_MD("is_shortcut","event"),&ShortCut::is_shortcut);
+ ObjectTypeDB::bind_method(_MD("get_as_text"),&ShortCut::get_as_text);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INPUT_EVENT,"shortcut"),_SCS("set_shortcut"),_SCS("get_shortcut"));
+}
+
+ShortCut::ShortCut(){
+
+}
diff --git a/scene/gui/input_action.h b/scene/gui/input_action.h
new file mode 100644
index 0000000000..8e0e1ef0bd
--- /dev/null
+++ b/scene/gui/input_action.h
@@ -0,0 +1,26 @@
+#ifndef INPUTACTION_H
+#define INPUTACTION_H
+
+#include "resource.h"
+
+class ShortCut : public Resource {
+
+ OBJ_TYPE(ShortCut,Resource);
+
+ InputEvent shortcut;
+protected:
+
+ static void _bind_methods();
+public:
+
+ void set_shortcut(const InputEvent& p_shortcut);
+ InputEvent get_shortcut() const;
+ bool is_shortcut(const InputEvent& p_Event) const;
+ bool is_valid() const;
+
+ String get_as_text() const;
+
+ ShortCut();
+};
+
+#endif // INPUTACTION_H
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 171dd94bfa..fc4ab5f8ca 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -297,6 +297,7 @@ void ItemList::remove_item(int p_idx){
items.remove(p_idx);
update();
shape_changed=true;
+ defer_select_single=-1;
}
@@ -307,6 +308,7 @@ void ItemList::clear(){
current=-1;
ensure_selected_visible=false;
update();
+ defer_select_single=-1;
}
@@ -323,6 +325,18 @@ int ItemList::get_fixed_column_width() const{
return fixed_column_width;
}
+void ItemList::set_same_column_width(bool p_enable){
+
+ same_column_width=p_enable;
+ update();
+ shape_changed=true;
+
+}
+int ItemList::is_same_column_width() const{
+
+ return same_column_width;
+}
+
void ItemList::set_max_text_lines(int p_lines){
ERR_FAIL_COND(p_lines<1);
@@ -381,6 +395,17 @@ Size2 ItemList::get_min_icon_size() const {
return min_icon_size;
}
+
+void ItemList::set_max_icon_size(const Size2& p_size) {
+
+ max_icon_size=p_size;
+ update();
+}
+
+Size2 ItemList::get_max_icon_size() const {
+
+ return max_icon_size;
+}
Size2 ItemList::Item::get_icon_size() const {
if (icon.is_null())
@@ -392,7 +417,23 @@ Size2 ItemList::Item::get_icon_size() const {
}
void ItemList::_input_event(const InputEvent& p_event) {
- if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_LEFT && p_event.mouse_button.pressed) {
+
+ if (defer_select_single>=0 && p_event.type==InputEvent::MOUSE_MOTION) {
+ defer_select_single=-1;
+ return;
+ }
+
+ if (defer_select_single>=0 && p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_LEFT && !p_event.mouse_button.pressed) {
+
+ select(defer_select_single,true);
+
+
+ emit_signal("multi_selected",defer_select_single,true);
+ defer_select_single=-1;
+ return;
+ }
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON && (p_event.mouse_button.button_index==BUTTON_LEFT || (allow_rmb_select && p_event.mouse_button.button_index==BUTTON_RIGHT)) && p_event.mouse_button.pressed) {
const InputEventMouseButton &mb = p_event.mouse_button;
@@ -431,6 +472,7 @@ void ItemList::_input_event(const InputEvent& p_event) {
if (select_mode==SELECT_MULTI && items[i].selected && mb.mod.command) {
unselect(i);
emit_signal("multi_selected",i,false);
+
} else if (select_mode==SELECT_MULTI && mb.mod.shift && current>=0 && current<items.size() && current!=i) {
int from = current;
@@ -444,23 +486,45 @@ void ItemList::_input_event(const InputEvent& p_event) {
if (selected)
emit_signal("multi_selected",i,true);
}
+
+ if (p_event.mouse_button.button_index==BUTTON_RIGHT) {
+
+ emit_signal("item_rmb_selected",i,Vector2(mb.x,mb.y));
+ }
} else {
- bool selected = !items[i].selected;
- select(i,select_mode==SELECT_SINGLE || !mb.mod.command);
- if (selected) {
- if (select_mode==SELECT_SINGLE) {
- emit_signal("item_selected",i);
- } else
- emit_signal("multi_selected",i,true);
+
+ if (!mb.doubleclick && !mb.mod.command && select_mode==SELECT_MULTI && items[i].selectable && items[i].selected && p_event.mouse_button.button_index==BUTTON_LEFT) {
+ defer_select_single=i;
+ return;
}
- if (/*select_mode==SELECT_SINGLE &&*/ mb.doubleclick) {
+ if (items[i].selected && p_event.mouse_button.button_index==BUTTON_RIGHT) {
- emit_signal("item_activated",i);
+ emit_signal("item_rmb_selected",i,Vector2(mb.x,mb.y));
+ } else {
+ bool selected = !items[i].selected;
+
+
+ select(i,select_mode==SELECT_SINGLE || !mb.mod.command);
+
+ if (selected) {
+ if (select_mode==SELECT_SINGLE) {
+ emit_signal("item_selected",i);
+ } else
+ emit_signal("multi_selected",i,true);
- }
+ }
+ if (p_event.mouse_button.button_index==BUTTON_RIGHT) {
+
+ emit_signal("item_rmb_selected",i,Vector2(mb.x,mb.y));
+ } else if (/*select_mode==SELECT_SINGLE &&*/ mb.doubleclick) {
+
+ emit_signal("item_activated",i);
+
+ }
+ }
}
@@ -668,6 +732,55 @@ void ItemList::ensure_current_is_visible() {
update();
}
+static Size2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
+
+ if (p_max_size.x<=0)
+ p_max_size.x=1e20;
+ if (p_max_size.y<=0)
+ p_max_size.y=1e20;
+
+
+ Size2 new_size;
+
+ if (p_size.x > p_max_size.x) {
+
+ new_size.width=p_max_size.x;
+ new_size.height=p_size.height * p_max_size.width / p_size.width;
+
+ if (new_size.height > p_max_size.height) {
+ new_size=Size2(); //invalid
+ }
+ }
+
+
+ if (p_size.y > p_max_size.y) {
+
+ Size2 new_size2;
+ new_size2.height=p_max_size.y;
+ new_size2.width=p_size.width * p_max_size.height / p_size.height;
+
+ if (new_size2.width < p_max_size.width) {
+
+ if (new_size!=Size2()) {
+
+ if (new_size2.x*new_size2.y > new_size.x*new_size.y) {
+ new_size=new_size2;
+ }
+ } else {
+ new_size=new_size2;
+ }
+ }
+
+ }
+
+ if (new_size==Size2())
+ return p_size;
+ else
+ return new_size;
+
+
+}
+
void ItemList::_notification(int p_what) {
if (p_what==NOTIFICATION_RESIZED) {
@@ -723,6 +836,8 @@ void ItemList::_notification(int p_what) {
}
if (shape_changed) {
+
+ float max_column_width = 0;
//1- compute item minimum sizes
for(int i=0;i<items.size();i++) {
@@ -730,12 +845,7 @@ void ItemList::_notification(int p_what) {
Size2 minsize;
if (items[i].icon.is_valid()) {
- minsize=items[i].get_icon_size();
-
- if (min_icon_size.x!=0)
- minsize.x = MAX(minsize.x,min_icon_size.x);
- if (min_icon_size.y!=0)
- minsize.y = MAX(minsize.y,min_icon_size.y);
+ minsize=_adjust_to_max_size(items[i].get_icon_size(),max_icon_size) * icon_scale;
if (items[i].text!="") {
if (icon_mode==ICON_MODE_TOP) {
@@ -768,10 +878,11 @@ void ItemList::_notification(int p_what) {
}
-
- items[i].rect_cache.size=minsize;
if (fixed_column_width>0)
- items[i].rect_cache.size.x=fixed_column_width;
+ minsize.x=fixed_column_width;
+ max_column_width=MAX(max_column_width,minsize.x);
+ items[i].rect_cache.size=minsize;
+ items[i].min_rect_cache.size=minsize;
}
@@ -800,17 +911,23 @@ void ItemList::_notification(int p_what) {
break;
}
+ items[i].rect_cache=items[i].min_rect_cache;
+ if(same_column_width)
+ items[i].rect_cache.size.x=max_column_width;
items[i].rect_cache.pos=ofs;
max_h=MAX(max_h,items[i].rect_cache.size.y);
- ofs.x+=items[i].rect_cache.size.x;
+ ofs.x+=items[i].rect_cache.size.x + hseparation;
//print_line("item "+itos(i)+" ofs "+rtos(items[i].rect_cache.size.x));
- if (col>0)
- ofs.x+=hseparation;
col++;
if (col==current_columns) {
if (i<items.size()-1)
separators.push_back(ofs.y+max_h+vseparation/2);
+
+ for(int j=i;j>=0 && col>0;j--, col--) {
+ items[j].rect_cache.size.y = max_h;
+ }
+
ofs.x=0;
ofs.y+=max_h+vseparation;
col=0;
@@ -818,6 +935,10 @@ void ItemList::_notification(int p_what) {
}
}
+ for(int j=items.size()-1;j>=0 && col>0;j--, col--) {
+ items[j].rect_cache.size.y = max_h;
+ }
+
if (all_fit) {
float max = MAX(page,ofs.y+max_h);
scroll_bar->set_max(max);
@@ -880,7 +1001,7 @@ void ItemList::_notification(int p_what) {
Vector2 text_ofs;
if (items[i].icon.is_valid()) {
- Size2 icon_size = items[i].get_icon_size();
+ Size2 icon_size = _adjust_to_max_size(items[i].get_icon_size(),max_icon_size) * icon_scale;
Vector2 icon_ofs;
if (min_icon_size!=Vector2()) {
@@ -892,7 +1013,12 @@ void ItemList::_notification(int p_what) {
if (icon_mode==ICON_MODE_TOP) {
pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width)/2);
+ pos.y += MIN(
+ Math::floor((items[i].rect_cache.size.height - icon_size.height)/2),
+ items[i].rect_cache.size.height - items[i].min_rect_cache.size.height
+ );
text_ofs.y = MAX(icon_size.height, min_icon_size.y) + icon_margin;
+ text_ofs.y += items[i].rect_cache.size.height - items[i].min_rect_cache.size.height;
} else {
pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height)/2);
@@ -900,7 +1026,7 @@ void ItemList::_notification(int p_what) {
}
if (items[i].icon_region.has_no_area())
- draw_texture(items[i].icon, pos);
+ draw_texture_rect(items[i].icon, Rect2(pos,icon_size) );
else
draw_texture_rect_region(items[i].icon, Rect2(pos, icon_size), items[i].icon_region);
@@ -918,6 +1044,8 @@ void ItemList::_notification(int p_what) {
Vector2 size = font->get_string_size(items[i].text);
if (fixed_column_width)
max_len=fixed_column_width;
+ else if(same_column_width)
+ max_len=items[i].rect_cache.size.x;
else
max_len=size.x;
@@ -1022,8 +1150,7 @@ void ItemList::_scroll_changed(double) {
update();
}
-
-String ItemList::get_tooltip(const Point2& p_pos) const {
+int ItemList::get_item_at_pos(const Point2& p_pos, bool p_exact) const {
Vector2 pos=p_pos;
Ref<StyleBox> bg = get_stylebox("bg");
@@ -1046,12 +1173,19 @@ String ItemList::get_tooltip(const Point2& p_pos) const {
}
float dist = rc.distance_to(pos);
- if (dist<closest_dist) {
+ if (!p_exact && dist<closest_dist) {
closest=i;
closest_dist=dist;
}
}
+ return closest;
+}
+
+String ItemList::get_tooltip(const Point2& p_pos) const {
+
+ int closest = get_item_at_pos(p_pos);
+
if (closest!=-1) {
if (items[closest].tooltip!="") {
return items[closest].tooltip;
@@ -1062,8 +1196,6 @@ String ItemList::get_tooltip(const Point2& p_pos) const {
}
return Control::get_tooltip(p_pos);
-
-
}
void ItemList::sort_items_by_text() {
@@ -1091,9 +1223,27 @@ int ItemList::find_metadata(const Variant& p_metadata) const {
}
+
+void ItemList::set_allow_rmb_select(bool p_allow) {
+ allow_rmb_select=p_allow;
+}
+
+bool ItemList::get_allow_rmb_select() const {
+
+ return allow_rmb_select;
+}
+
+void ItemList::set_icon_scale(real_t p_scale) {
+ icon_scale = p_scale;
+}
+
+real_t ItemList::get_icon_scale() const {
+ return icon_scale;
+}
+
void ItemList::_bind_methods(){
- ObjectTypeDB::bind_method(_MD("add_item","text","icon:Texture","selectable"),&ItemList::add_item,DEFVAL(Ref<Texture>()),DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("add_item","text","icon:Texture","selectable"),&ItemList::add_item,DEFVAL(Variant()),DEFVAL(true));
ObjectTypeDB::bind_method(_MD("add_icon_item","icon:Texture","selectable"),&ItemList::add_icon_item,DEFVAL(true));
ObjectTypeDB::bind_method(_MD("set_item_text","idx","text"),&ItemList::set_item_text);
@@ -1133,6 +1283,9 @@ void ItemList::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_fixed_column_width","width"),&ItemList::set_fixed_column_width);
ObjectTypeDB::bind_method(_MD("get_fixed_column_width"),&ItemList::get_fixed_column_width);
+ ObjectTypeDB::bind_method(_MD("set_same_column_width","enable"),&ItemList::set_same_column_width);
+ ObjectTypeDB::bind_method(_MD("is_same_column_width"),&ItemList::is_same_column_width);
+
ObjectTypeDB::bind_method(_MD("set_max_text_lines","lines"),&ItemList::set_max_text_lines);
ObjectTypeDB::bind_method(_MD("get_max_text_lines"),&ItemList::get_max_text_lines);
@@ -1148,6 +1301,17 @@ void ItemList::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_min_icon_size","size"),&ItemList::set_min_icon_size);
ObjectTypeDB::bind_method(_MD("get_min_icon_size"),&ItemList::get_min_icon_size);
+ ObjectTypeDB::bind_method(_MD("set_max_icon_size","size"),&ItemList::set_max_icon_size);
+ ObjectTypeDB::bind_method(_MD("get_max_icon_size"),&ItemList::get_max_icon_size);
+
+ ObjectTypeDB::bind_method(_MD("set_icon_scale","scale"),&ItemList::set_icon_scale);
+ ObjectTypeDB::bind_method(_MD("get_icon_scale"),&ItemList::get_icon_scale);
+
+ ObjectTypeDB::bind_method(_MD("set_allow_rmb_select","allow"),&ItemList::set_allow_rmb_select);
+ ObjectTypeDB::bind_method(_MD("get_allow_rmb_select"),&ItemList::get_allow_rmb_select);
+
+ ObjectTypeDB::bind_method(_MD("get_item_at_pos","pos","exact"),&ItemList::get_item_at_pos,DEFVAL(false));
+
ObjectTypeDB::bind_method(_MD("ensure_current_is_visible"),&ItemList::ensure_current_is_visible);
ObjectTypeDB::bind_method(_MD("_scroll_changed"),&ItemList::_scroll_changed);
@@ -1159,6 +1323,7 @@ void ItemList::_bind_methods(){
BIND_CONSTANT( SELECT_MULTI );
ADD_SIGNAL( MethodInfo("item_selected",PropertyInfo(Variant::INT,"index")));
+ ADD_SIGNAL( MethodInfo("item_rmb_selected",PropertyInfo(Variant::INT,"index"),PropertyInfo(Variant::VECTOR2,"atpos")));
ADD_SIGNAL( MethodInfo("multi_selected",PropertyInfo(Variant::INT,"index"),PropertyInfo(Variant::BOOL,"selected")));
ADD_SIGNAL( MethodInfo("item_activated",PropertyInfo(Variant::INT,"index")));
}
@@ -1173,6 +1338,7 @@ ItemList::ItemList() {
icon_mode=ICON_MODE_LEFT;
fixed_column_width=0;
+ same_column_width = false;
max_text_lines=1;
max_columns=1;
@@ -1186,7 +1352,10 @@ ItemList::ItemList() {
current_columns=1;
search_time_msec=0;
ensure_selected_visible=false;
+ defer_select_single=-1;
+ allow_rmb_select=false;
+ icon_scale = 1.0f;
}
ItemList::~ItemList() {
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index c9c575fd54..087c585128 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -33,6 +33,7 @@ private:
Color custom_bg;
Rect2 rect_cache;
+ Rect2 min_rect_cache;
Size2 get_icon_size() const;
@@ -44,6 +45,7 @@ private:
bool shape_changed;
bool ensure_selected_visible;
+ bool same_column_width;
Vector<Item> items;
Vector<int> separators;
@@ -59,7 +61,17 @@ private:
int fixed_column_width;
int max_text_lines;
int max_columns;
+
Size2 min_icon_size;
+ Size2 max_icon_size;
+
+ Size2 max_item_size_cache;
+
+ int defer_select_single;
+
+ bool allow_rmb_select;
+
+ real_t icon_scale;
void _scroll_changed(double);
void _input_event(const InputEvent& p_event);
@@ -69,6 +81,7 @@ protected:
static void _bind_methods();
public:
+
void add_item(const String& p_item,const Ref<Texture>& p_texture=Ref<Texture>(),bool p_selectable=true);
void add_icon_item(const Ref<Texture>& p_item,bool p_selectable=true);
@@ -116,6 +129,9 @@ public:
void set_fixed_column_width(int p_size);
int get_fixed_column_width() const;
+ void set_same_column_width(bool p_enable);
+ int is_same_column_width() const;
+
void set_max_text_lines(int p_amount);
int get_max_text_lines() const;
@@ -131,12 +147,22 @@ public:
void set_min_icon_size(const Size2& p_size);
Size2 get_min_icon_size() const;
+ void set_max_icon_size(const Size2& p_size);
+ Size2 get_max_icon_size() const;
+
+ void set_allow_rmb_select(bool p_allow);
+ bool get_allow_rmb_select() const;
+
void ensure_current_is_visible();
void sort_items_by_text();
int find_metadata(const Variant& p_metadata) const;
virtual String get_tooltip(const Point2& p_pos) const;
+ int get_item_at_pos(const Point2& p_pos,bool p_exact=false) const;
+
+ void set_icon_scale(real_t p_scale);
+ real_t get_icon_scale() const;
ItemList();
~ItemList();
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index e8097c79a4..09c6a77b42 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -87,10 +87,11 @@ void Label::_notification(int p_what) {
Color font_color_shadow = get_color("font_color_shadow");
bool use_outlinde = get_constant("shadow_as_outline");
Point2 shadow_ofs(get_constant("shadow_offset_x"),get_constant("shadow_offset_y"));
+ int line_spacing = get_constant("line_spacing");
VisualServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(),font.is_valid() && font->is_distance_field_hint());
- int font_h = font->get_height();
+ int font_h = font->get_height()+line_spacing;
int lines_visible = size.y/font_h;
int space_w=font->get_char_size(' ').width;
int chars_total=0;
@@ -372,6 +373,7 @@ void Label::regenerate_word_cache() {
int line_width=0;
int space_count=0;
int space_width=font->get_char_size(' ').width;
+ int line_spacing = get_constant("line_spacing");
line_count=1;
total_char_cache=0;
@@ -486,9 +488,9 @@ void Label::regenerate_word_cache() {
if (!autowrap) {
minsize.width=width;
if (max_lines_visible > 0 && line_count > max_lines_visible) {
- minsize.height=font->get_height()*max_lines_visible;
+ minsize.height=(font->get_height()+line_spacing)*max_lines_visible;
} else {
- minsize.height=font->get_height()*line_count;
+ minsize.height=(font->get_height()+line_spacing)*line_count;
}
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 21dee62b38..44cc798447 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -41,7 +41,15 @@ void LineEdit::_input_event(InputEvent p_event) {
const InputEventMouseButton &b = p_event.mouse_button;
- if (b.button_index!=1)
+ if (b.pressed && b.button_index==BUTTON_RIGHT) {
+ menu->set_pos(get_global_transform().xform(get_local_mouse_pos()));
+ menu->set_size(Vector2(1,1));
+ menu->popup();
+ grab_focus();
+ return;
+ }
+
+ if (b.button_index!=BUTTON_LEFT)
break;
if (b.pressed) {
@@ -143,24 +151,10 @@ void LineEdit::_input_event(InputEvent p_event) {
if( k.mod.command && editable) {
- int old_cursor_pos = cursor_pos;
- text = undo_text;
-
- Ref<Font> font = get_font("font");
-
- cached_width = 0;
- for (int i = 0; i<text.length(); i++)
- cached_width += font->get_char_size(text[i]).width;
+ undo();
- if(old_cursor_pos > text.length()) {
- set_cursor_pos(text.length());
- } else {
- set_cursor_pos(old_cursor_pos);
- }
}
- emit_signal("text_changed",text);
- _change_notify("text");
} break;
@@ -460,7 +454,7 @@ void LineEdit::_notification(int p_what) {
} break;
}
- int ofs_max=width-style->get_minimum_size().width;
+ int ofs_max=width-style->get_minimum_size().width+x_ofs;
int char_ofs=window_pos;
int y_area=height-style->get_minimum_size().height;
@@ -559,6 +553,28 @@ void LineEdit::paste_text() {
}
+void LineEdit::undo() {
+
+ int old_cursor_pos = cursor_pos;
+ text = undo_text;
+
+ Ref<Font> font = get_font("font");
+
+ cached_width = 0;
+ for (int i = 0; i<text.length(); i++)
+ cached_width += font->get_char_size(text[i]).width;
+
+ if(old_cursor_pos > text.length()) {
+ set_cursor_pos(text.length());
+ } else {
+ set_cursor_pos(old_cursor_pos);
+ }
+
+ emit_signal("text_changed",text);
+ _change_notify("text");
+
+}
+
void LineEdit::shift_selection_check_pre(bool p_shift) {
if (!selection.old_shift && p_shift) {
@@ -669,6 +685,8 @@ void LineEdit::set_text(String p_text) {
void LineEdit::clear() {
clear_internal();
+ emit_signal("text_changed",text);
+ _change_notify("text");
}
String LineEdit::get_text() const {
@@ -781,8 +799,7 @@ Size2 LineEdit::get_minimum_size() const {
Ref<Font> font=get_font("font");
Size2 min=style->get_minimum_size();
- min.height+=font->get_height();
- min.width+=get_constant("minimum_spaces")*font->get_char_size(' ').x;
+ min+=font->get_string_size(this->text);
return min;
}
@@ -932,6 +949,39 @@ bool LineEdit::is_text_field() const {
return true;
}
+void LineEdit::menu_option(int p_option) {
+
+ switch(p_option) {
+ case MENU_CUT: {
+ cut_text();
+ } break;
+ case MENU_COPY: {
+
+ copy_text();
+ } break;
+ case MENU_PASTE: {
+
+ paste_text();
+ } break;
+ case MENU_CLEAR: {
+ clear();
+ } break;
+ case MENU_SELECT_ALL: {
+ select_all();
+ } break;
+ case MENU_UNDO: {
+
+ undo();
+ } break;
+
+ }
+
+}
+
+PopupMenu *LineEdit::get_menu() const {
+ return menu;
+}
+
void LineEdit::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_align", "align"), &LineEdit::set_align);
@@ -952,6 +1002,8 @@ void LineEdit::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_secret","enabled"),&LineEdit::set_secret);
ObjectTypeDB::bind_method(_MD("is_secret"),&LineEdit::is_secret);
ObjectTypeDB::bind_method(_MD("select","from","to"),&LineEdit::select,DEFVAL(0),DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("menu_option","option"),&LineEdit::menu_option);
+ ObjectTypeDB::bind_method(_MD("get_menu:PopupMenu"),&LineEdit::get_menu);
ADD_SIGNAL( MethodInfo("text_changed", PropertyInfo( Variant::STRING, "text" )) );
ADD_SIGNAL( MethodInfo("text_entered", PropertyInfo( Variant::STRING, "text" )) );
@@ -961,11 +1013,21 @@ void LineEdit::_bind_methods() {
BIND_CONSTANT(ALIGN_RIGHT);
BIND_CONSTANT(ALIGN_FILL);
- ADD_PROPERTY( PropertyInfo( Variant::STRING, "text" ), _SCS("set_text"),_SCS("get_text") );
+ BIND_CONSTANT( MENU_CUT );
+ BIND_CONSTANT( MENU_COPY );
+ BIND_CONSTANT( MENU_PASTE );
+ BIND_CONSTANT( MENU_CLEAR );
+ BIND_CONSTANT( MENU_SELECT_ALL );
+ BIND_CONSTANT( MENU_UNDO );
+ BIND_CONSTANT( MENU_MAX );
+
+ ADD_PROPERTYNZ( PropertyInfo( Variant::STRING, "text" ), _SCS("set_text"),_SCS("get_text") );
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), _SCS("set_align"), _SCS("get_align"));
- ADD_PROPERTY( PropertyInfo( Variant::INT, "max_length" ), _SCS("set_max_length"),_SCS("get_max_length") );
- ADD_PROPERTY( PropertyInfo( Variant::BOOL, "editable" ), _SCS("set_editable"),_SCS("is_editable") );
- ADD_PROPERTY( PropertyInfo( Variant::BOOL, "secret" ), _SCS("set_secret"),_SCS("is_secret") );
+ ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "max_length" ), _SCS("set_max_length"),_SCS("get_max_length") );
+ ADD_PROPERTYNO( PropertyInfo( Variant::BOOL, "editable" ), _SCS("set_editable"),_SCS("is_editable") );
+ ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "secret" ), _SCS("set_secret"),_SCS("is_secret") );
+ ADD_PROPERTY( PropertyInfo( Variant::INT,"focus_mode", PROPERTY_HINT_ENUM, "None,Click,All" ), _SCS("set_focus_mode"), _SCS("get_focus_mode") );
+
}
LineEdit::LineEdit() {
@@ -984,6 +1046,20 @@ LineEdit::LineEdit() {
set_stop_mouse(true);
+ menu = memnew( PopupMenu );
+ add_child(menu);
+ menu->add_item(TTR("Cut"),MENU_CUT,KEY_MASK_CMD|KEY_X);
+ menu->add_item(TTR("Copy"),MENU_COPY,KEY_MASK_CMD|KEY_C);
+ menu->add_item(TTR("Paste"),MENU_PASTE,KEY_MASK_CMD|KEY_V);
+ menu->add_separator();
+ menu->add_item(TTR("Select All"),MENU_SELECT_ALL,KEY_MASK_CMD|KEY_A);
+ menu->add_item(TTR("Clear"),MENU_CLEAR);
+ menu->add_separator();
+ menu->add_item(TTR("Undo"),MENU_UNDO,KEY_MASK_CMD|KEY_Z);
+ menu->connect("item_pressed",this,"menu_option");
+
+
+
}
LineEdit::~LineEdit() {
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 207c6b115b..586a54e950 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -30,6 +30,8 @@
#define LINE_EDIT_H
#include "scene/gui/control.h"
+#include "scene/gui/popup_menu.h"
+
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -45,6 +47,18 @@ public:
ALIGN_RIGHT,
ALIGN_FILL
};
+
+ enum MenuItems {
+ MENU_CUT,
+ MENU_COPY,
+ MENU_PASTE,
+ MENU_CLEAR,
+ MENU_SELECT_ALL,
+ MENU_UNDO,
+ MENU_MAX
+
+ };
+
private:
Align align;
@@ -54,6 +68,8 @@ private:
String undo_text;
String text;
+ PopupMenu *menu;
+
int cursor_pos;
int window_pos;
int max_length; // 0 for no maximum
@@ -85,14 +101,12 @@ private:
void clear_internal();
void changed_internal();
- void copy_text();
- void cut_text();
- void paste_text();
void _input_event(InputEvent p_event);
void _notification(int p_what);
+
protected:
static void _bind_methods();
public:
@@ -103,6 +117,8 @@ public:
virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const;
virtual void drop_data(const Point2& p_point,const Variant& p_data);
+ void menu_option(int p_option);
+ PopupMenu *get_menu() const;
void select_all();
@@ -116,6 +132,10 @@ public:
void append_at_cursor(String p_text);
void clear();
+ void copy_text();
+ void cut_text();
+ void paste_text();
+ void undo();
void set_editable(bool p_editable);
bool is_editable() const;
@@ -127,7 +147,7 @@ public:
virtual Size2 get_minimum_size() const;
- virtual bool is_text_field() const;
+ virtual bool is_text_field() const;
LineEdit();
~LineEdit();
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 007d0a709e..065423ae2d 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -119,6 +119,6 @@ void LinkButton::_bind_methods() {
LinkButton::LinkButton() {
underline_mode=UNDERLINE_MODE_ALWAYS;
- set_focus_mode(FOCUS_NONE);
+ set_enabled_focus_mode(FOCUS_NONE);
set_default_cursor_shape(CURSOR_POINTING_HAND);
}
diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp
index fde5df6b72..5f798f445c 100644
--- a/scene/gui/margin_container.cpp
+++ b/scene/gui/margin_container.cpp
@@ -31,7 +31,10 @@
Size2 MarginContainer::get_minimum_size() const {
- int margin = get_constant("margin");
+ int margin_left = get_constant("margin_left");
+ int margin_top = get_constant("margin_top");
+ int margin_right = get_constant("margin_right");
+ int margin_bottom = get_constant("margin_bottom");
Size2 max;
@@ -52,9 +55,10 @@ Size2 MarginContainer::get_minimum_size() const {
max.height = s.height;
}
- max.width += margin;
- return max;
+ max.width += (margin_left + margin_right);
+ max.height += (margin_top + margin_bottom);
+ return max;
}
@@ -62,7 +66,10 @@ void MarginContainer::_notification(int p_what) {
if (p_what==NOTIFICATION_SORT_CHILDREN) {
- int margin = get_constant("margin");
+ int margin_left = get_constant("margin_left");
+ int margin_top = get_constant("margin_top");
+ int margin_right = get_constant("margin_right");
+ int margin_bottom = get_constant("margin_bottom");
Size2 s = get_size();
@@ -73,13 +80,10 @@ void MarginContainer::_notification(int p_what) {
continue;
if (c->is_set_as_toplevel())
continue;
- int w;
- if (c->get_h_size_flags() & Control::SIZE_EXPAND)
- w=s.width-margin;
- else
- w=c->get_combined_minimum_size().width;
- fit_child_in_rect(c,Rect2(margin,0,s.width-margin,s.height));
+ int w=s.width-margin_left-margin_right;
+ int h=s.height-margin_top-margin_bottom;
+ fit_child_in_rect(c,Rect2(margin_left,margin_top,w,h));
}
}
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index cb8806e2ef..28d67287d5 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -32,46 +32,15 @@
void MenuButton::_unhandled_key_input(InputEvent p_event) {
- //check accelerators
- if (p_event.type==InputEvent::KEY && p_event.key.pressed) {
+ if (p_event.is_pressed() && !p_event.is_echo() && (p_event.type==InputEvent::KEY || p_event.type==InputEvent::ACTION || p_event.type==InputEvent::JOYSTICK_BUTTON)) {
if (!get_parent() || !is_visible() || is_disabled())
return;
- uint32_t code=p_event.key.scancode;
- if (code==0)
- code=p_event.key.unicode;
- if (p_event.key.mod.control)
- code|=KEY_MASK_CTRL;
- if (p_event.key.mod.alt)
- code|=KEY_MASK_ALT;
- if (p_event.key.mod.meta)
- code|=KEY_MASK_META;
- if (p_event.key.mod.shift)
- code|=KEY_MASK_SHIFT;
-
-
- int item = popup->find_item_by_accelerator(code);
-
-
- if (item>=0 && ! popup->is_item_disabled(item))
- popup->activate_item(item);
- /*
- for(int i=0;i<items.size();i++) {
-
-
- if (items[i].accel==0)
- continue;
-
- if (items[i].accel==code) {
-
- emit_signal("item_pressed",items[i].ID);
- }
- }*/
+ int item = popup->activate_item_by_event(p_event);
}
-
}
@@ -139,7 +108,7 @@ MenuButton::MenuButton() {
set_flat(true);
- set_focus_mode(FOCUS_NONE);
+ set_enabled_focus_mode(FOCUS_NONE);
popup = memnew( PopupMenu );
popup->hide();
add_child(popup);
diff --git a/scene/gui/patch_9_frame.cpp b/scene/gui/patch_9_frame.cpp
index b6e261714c..3ecee7328b 100644
--- a/scene/gui/patch_9_frame.cpp
+++ b/scene/gui/patch_9_frame.cpp
@@ -9,10 +9,9 @@ void Patch9Frame::_notification(int p_what) {
if (texture.is_null())
return;
-
Size2 s=get_size();
RID ci = get_canvas_item();
- VS::get_singleton()->canvas_item_add_style_box(ci,Rect2(Point2(),s),texture->get_rid(),Vector2(margin[MARGIN_LEFT],margin[MARGIN_TOP]),Vector2(margin[MARGIN_RIGHT],margin[MARGIN_BOTTOM]),draw_center,modulate);
+ VS::get_singleton()->canvas_item_add_style_box(ci,Rect2(Point2(),s),region_rect,texture->get_rid(),Vector2(margin[MARGIN_LEFT],margin[MARGIN_TOP]),Vector2(margin[MARGIN_RIGHT],margin[MARGIN_BOTTOM]),draw_center,modulate);
// draw_texture_rect(texture,Rect2(Point2(),s),false,modulate);
/*
@@ -47,12 +46,15 @@ void Patch9Frame::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_modulate"), & Patch9Frame::get_modulate );
ObjectTypeDB::bind_method(_MD("set_patch_margin","margin","value"), & Patch9Frame::set_patch_margin );
ObjectTypeDB::bind_method(_MD("get_patch_margin","margin"), & Patch9Frame::get_patch_margin );
+ ObjectTypeDB::bind_method(_MD("set_region_rect","rect"),&Patch9Frame::set_region_rect);
+ ObjectTypeDB::bind_method(_MD("get_region_rect"),&Patch9Frame::get_region_rect);
ObjectTypeDB::bind_method(_MD("set_draw_center","draw_center"), & Patch9Frame::set_draw_center );
ObjectTypeDB::bind_method(_MD("get_draw_center"), & Patch9Frame::get_draw_center );
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"),_SCS("get_texture") );
ADD_PROPERTYNO( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate") );
ADD_PROPERTYNO( PropertyInfo( Variant::BOOL, "draw_center"), _SCS("set_draw_center"),_SCS("get_draw_center") );
+ ADD_PROPERTYNZ( PropertyInfo( Variant::RECT2, "region_rect"), _SCS("set_region_rect"),_SCS("get_region_rect"));
ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/left",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_LEFT );
ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/top",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_TOP );
ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/right",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_RIGHT );
@@ -93,6 +95,20 @@ void Patch9Frame::set_patch_margin(Margin p_margin,int p_size) {
margin[p_margin]=p_size;
update();
minimum_size_changed();
+ switch (p_margin) {
+ case MARGIN_LEFT:
+ _change_notify("patch_margin/left");
+ break;
+ case MARGIN_TOP:
+ _change_notify("patch_margin/top");
+ break;
+ case MARGIN_RIGHT:
+ _change_notify("patch_margin/right");
+ break;
+ case MARGIN_BOTTOM:
+ _change_notify("patch_margin/bottom");
+ break;
+ }
}
int Patch9Frame::get_patch_margin(Margin p_margin) const{
@@ -101,6 +117,22 @@ int Patch9Frame::get_patch_margin(Margin p_margin) const{
return margin[p_margin];
}
+void Patch9Frame::set_region_rect(const Rect2& p_region_rect) {
+
+ if (region_rect==p_region_rect)
+ return;
+
+ region_rect=p_region_rect;
+
+ item_rect_changed();
+ _change_notify("region_rect");
+}
+
+Rect2 Patch9Frame::get_region_rect() const {
+
+ return region_rect;
+}
+
void Patch9Frame::set_draw_center(bool p_draw) {
draw_center=p_draw;
@@ -128,5 +160,3 @@ Patch9Frame::Patch9Frame() {
Patch9Frame::~Patch9Frame()
{
}
-
-
diff --git a/scene/gui/patch_9_frame.h b/scene/gui/patch_9_frame.h
index 562a5b1d77..52e2324c3d 100644
--- a/scene/gui/patch_9_frame.h
+++ b/scene/gui/patch_9_frame.h
@@ -11,6 +11,7 @@ class Patch9Frame : public Control {
bool draw_center;
int margin[4];
+ Rect2 region_rect;
Color modulate;
Ref<Texture> texture;
protected:
@@ -30,6 +31,9 @@ public:
void set_patch_margin(Margin p_margin,int p_size);
int get_patch_margin(Margin p_margin) const;
+ void set_region_rect(const Rect2& p_region_rect);
+ Rect2 get_region_rect() const;
+
void set_draw_center(bool p_enable);
bool get_draw_center() const;
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 0d9a76937c..8d02d0e4e5 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -44,6 +44,8 @@ void Popup::_notification(int p_what) {
notification(NOTIFICATION_POPUP_HIDE);
emit_signal("popup_hide");
}
+
+ update_configuration_warning();
}
if (p_what==NOTIFICATION_ENTER_TREE) {
@@ -282,6 +284,14 @@ Popup::Popup() {
hide();
}
+String Popup::get_configuration_warning() const {
+
+ if (is_visible()) {
+ return TTR("Popups will hide by default unless you call popup() or any of the popup*() functions. Making them visible for editing is fine though, but they will hide upon running.");
+ }
+
+ return String();
+}
Popup::~Popup()
{
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 8afcdc01db..dccaf2ae69 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -65,6 +65,7 @@ public:
void set_as_minsize();
virtual void popup();
+ virtual String get_configuration_warning() const;
Popup();
~Popup();
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 3329d24890..b3f18bf8fa 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -32,9 +32,16 @@
#include "translation.h"
#include "os/input.h"
-String PopupMenu::_get_accel_text(uint32_t p_accel) const {
+String PopupMenu::_get_accel_text(int p_item) const {
+
+ ERR_FAIL_INDEX_V(p_item,items.size(),String());
+
+ if (items[p_item].shortcut.is_valid())
+ return items[p_item].shortcut->get_as_text();
+ else if (items[p_item].accel)
+ return keycode_get_string(items[p_item].accel);
+ return String();
- return keycode_get_string(p_accel);
/*
String atxt;
if (p_accel&KEY_MASK_SHIFT)
@@ -87,14 +94,15 @@ Size2 PopupMenu::get_minimum_size() const {
size.width+=check_w+hseparation;
}
- size.width+=font->get_string_size(items[i].text).width;
+ String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].text;
+ size.width+=font->get_string_size(text).width;
if (i>0)
size.height+=vseparation;
- if (items[i].accel) {
+ if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) {
int accel_w = hseparation*2;
- accel_w+=font->get_string_size(_get_accel_text(items[i].accel)).width;
+ accel_w+=font->get_string_size(_get_accel_text(i)).width;
accel_max_w = MAX( accel_w, accel_max_w );
}
@@ -484,13 +492,15 @@ void PopupMenu::_notification(int p_what) {
}
item_ofs.y+=font->get_ascent();
- if (!items[i].separator)
- font->draw(ci,item_ofs+Point2(0,Math::floor((h-font_h)/2.0)),items[i].text,items[i].disabled?font_color_disabled:(i==mouse_over?font_color_hover:font_color));
+ String text = items[i].shortcut.is_valid() ? String(tr(items[i].shortcut->get_name())) : items[i].text;
+ if (!items[i].separator) {
+ font->draw(ci,item_ofs+Point2(0,Math::floor((h-font_h)/2.0)),text,items[i].disabled?font_color_disabled:(i==mouse_over?font_color_hover:font_color));
+ }
- if (items[i].accel) {
+ if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) {
//accelerator
- String text = _get_accel_text(items[i].accel);
+ String text = _get_accel_text(i);
item_ofs.x=size.width-style->get_margin(MARGIN_RIGHT)-font->get_string_size(text).width;
font->draw(ci,item_ofs+Point2(0,Math::floor((h-font_h)/2.0)),text,i==mouse_over?font_color_hover:font_color_accel);
@@ -570,6 +580,64 @@ void PopupMenu::add_check_item(const String& p_label,int p_ID,uint32_t p_accel)
update();
}
+
+void PopupMenu::add_icon_shortcut(const Ref<Texture>& p_icon,const Ref<ShortCut>& p_shortcut,int p_ID) {
+
+ ERR_FAIL_COND(p_shortcut.is_null());
+
+ _ref_shortcut(p_shortcut);
+
+ Item item;
+ item.ID=p_ID;
+ item.icon=p_icon;
+ item.shortcut=p_shortcut;
+ items.push_back(item);
+ update();
+
+}
+
+void PopupMenu::add_shortcut(const Ref<ShortCut>& p_shortcut,int p_ID){
+
+ ERR_FAIL_COND(p_shortcut.is_null());
+
+ _ref_shortcut(p_shortcut);
+
+ Item item;
+ item.ID=p_ID;
+ item.shortcut=p_shortcut;
+ items.push_back(item);
+ update();
+
+}
+void PopupMenu::add_icon_check_shortcut(const Ref<Texture>& p_icon,const Ref<ShortCut>& p_shortcut,int p_ID){
+
+ ERR_FAIL_COND(p_shortcut.is_null());
+
+ _ref_shortcut(p_shortcut);
+
+ Item item;
+ item.ID=p_ID;
+ item.shortcut=p_shortcut;
+ item.checkable=true;
+ item.icon=p_icon;
+ items.push_back(item);
+ update();
+}
+
+void PopupMenu::add_check_shortcut(const Ref<ShortCut>& p_shortcut,int p_ID){
+
+ ERR_FAIL_COND(p_shortcut.is_null());
+
+ _ref_shortcut(p_shortcut);
+
+ Item item;
+ item.ID=p_ID;
+ item.shortcut=p_shortcut;
+ item.checkable=true;
+ items.push_back(item);
+ update();
+}
+
void PopupMenu::set_item_text(int p_idx,const String& p_text) {
ERR_FAIL_INDEX(p_idx,items.size());
@@ -701,6 +769,12 @@ String PopupMenu::get_item_tooltip(int p_idx) const {
return items[p_idx].tooltip;
}
+Ref<ShortCut> PopupMenu::get_item_shortcut(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,items.size(),Ref<ShortCut>());
+ return items[p_idx].shortcut;
+}
+
void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
ERR_FAIL_INDEX(p_idx,items.size());
@@ -730,6 +804,21 @@ void PopupMenu::set_item_tooltip(int p_idx,const String& p_tooltip) {
update();
}
+void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut>& p_shortcut) {
+ ERR_FAIL_INDEX(p_idx,items.size());
+ if (items[p_idx].shortcut.is_valid()) {
+ _unref_shortcut(items[p_idx].shortcut);
+ }
+ items[p_idx].shortcut=p_shortcut;
+
+ if (items[p_idx].shortcut.is_valid()) {
+ _ref_shortcut(items[p_idx].shortcut);
+ }
+
+
+ update();
+}
+
bool PopupMenu::is_item_checkable(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx,items.size(),false);
return items[p_idx].checkable;
@@ -740,15 +829,55 @@ int PopupMenu::get_item_count() const {
return items.size();
}
-int PopupMenu::find_item_by_accelerator(uint32_t p_accel) const {
+bool PopupMenu::activate_item_by_event(const InputEvent& p_event) {
+
+ uint32_t code=0;
+ if (p_event.type==InputEvent::KEY) {
+ code=p_event.key.scancode;
+ if (code==0)
+ code=p_event.key.unicode;
+ if (p_event.key.mod.control)
+ code|=KEY_MASK_CTRL;
+ if (p_event.key.mod.alt)
+ code|=KEY_MASK_ALT;
+ if (p_event.key.mod.meta)
+ code|=KEY_MASK_META;
+ if (p_event.key.mod.shift)
+ code|=KEY_MASK_SHIFT;
+ }
+
int il=items.size();
for(int i=0;i<il;i++) {
+ if (is_item_disabled(i))
+ continue;
- if (items[i].accel==p_accel)
- return i;
+
+ if (items[i].shortcut.is_valid() && items[i].shortcut->is_shortcut(p_event)) {
+ activate_item(i);
+ return true;
+ }
+
+ if (code!=0 && items[i].accel==code) {
+ activate_item(i);
+ return true;
+ }
+
+ if (items[i].submenu!="") {
+ Node* n = get_node(items[i].submenu);
+ if(!n)
+ continue;
+
+ PopupMenu* pm = n->cast_to<PopupMenu>();
+ if(!pm)
+ continue;
+
+ if(pm->activate_item_by_event(p_event)) {
+ return true;
+ }
+ }
}
- return -1;
+ return false;
}
void PopupMenu::activate_item(int p_item) {
@@ -773,6 +902,12 @@ void PopupMenu::activate_item(int p_item) {
void PopupMenu::remove_item(int p_idx) {
+ ERR_FAIL_INDEX(p_idx,items.size());
+
+ if (items[p_idx].shortcut.is_valid()) {
+ _unref_shortcut(items[p_idx].shortcut);
+ }
+
items.remove(p_idx);
update();
}
@@ -788,6 +923,11 @@ void PopupMenu::add_separator() {
void PopupMenu::clear() {
+ for(int i=0;i<items.size();i++) {
+ if (items[i].shortcut.is_valid()) {
+ _unref_shortcut(items[i].shortcut);
+ }
+ }
items.clear();
mouse_over=-1;
update();
@@ -816,6 +956,27 @@ Array PopupMenu::_get_items() const {
return items;
}
+
+void PopupMenu::_ref_shortcut( Ref<ShortCut> p_sc) {
+
+ if (!shortcut_refcount.has(p_sc)) {
+ shortcut_refcount[p_sc]=1;
+ p_sc->connect("changed",this,"update");
+ } else {
+ shortcut_refcount[p_sc]+=1;
+ }
+}
+
+void PopupMenu::_unref_shortcut(Ref<ShortCut> p_sc) {
+
+ ERR_FAIL_COND(!shortcut_refcount.has(p_sc));
+ shortcut_refcount[p_sc]--;
+ if (shortcut_refcount[p_sc]==0) {
+ p_sc->disconnect("changed",this,"update");
+ shortcut_refcount.erase(p_sc);
+ }
+}
+
void PopupMenu::_set_items(const Array& p_items){
ERR_FAIL_COND(p_items.size() % 10);
@@ -894,12 +1055,20 @@ void PopupMenu::_bind_methods() {
ObjectTypeDB::bind_method(_MD("add_icon_check_item","texture","label","id","accel"),&PopupMenu::add_icon_check_item,DEFVAL(-1),DEFVAL(0));
ObjectTypeDB::bind_method(_MD("add_check_item","label","id","accel"),&PopupMenu::add_check_item,DEFVAL(-1),DEFVAL(0));
ObjectTypeDB::bind_method(_MD("add_submenu_item","label","submenu","id"),&PopupMenu::add_submenu_item,DEFVAL(-1));
+
+ ObjectTypeDB::bind_method(_MD("add_icon_shortcut","texture","shortcut:ShortCut","id"),&PopupMenu::add_icon_shortcut,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("add_shortcut","shortcut:ShortCut","id"),&PopupMenu::add_shortcut,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("add_icon_check_shortcut","texture","shortcut:ShortCut","id"),&PopupMenu::add_icon_check_shortcut,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("add_check_shortcut","shortcut:ShortCut","id"),&PopupMenu::add_check_shortcut,DEFVAL(-1));
+
+
ObjectTypeDB::bind_method(_MD("set_item_text","idx","text"),&PopupMenu::set_item_text);
ObjectTypeDB::bind_method(_MD("set_item_icon","idx","icon"),&PopupMenu::set_item_icon);
ObjectTypeDB::bind_method(_MD("set_item_accelerator","idx","accel"),&PopupMenu::set_item_accelerator);
ObjectTypeDB::bind_method(_MD("set_item_metadata","idx","metadata"),&PopupMenu::set_item_metadata);
ObjectTypeDB::bind_method(_MD("set_item_checked","idx","checked"),&PopupMenu::set_item_checked);
ObjectTypeDB::bind_method(_MD("set_item_disabled","idx","disabled"),&PopupMenu::set_item_disabled);
+ ObjectTypeDB::bind_method(_MD("set_item_shortcut","idx","shortcut:ShortCut"),&PopupMenu::set_item_shortcut);
ObjectTypeDB::bind_method(_MD("set_item_submenu","idx","submenu"),&PopupMenu::set_item_submenu);
ObjectTypeDB::bind_method(_MD("set_item_as_separator","idx","enable"),&PopupMenu::set_item_as_separator);
ObjectTypeDB::bind_method(_MD("set_item_as_checkable","idx","enable"),&PopupMenu::set_item_as_checkable);
@@ -908,6 +1077,7 @@ void PopupMenu::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_item_icon","idx"),&PopupMenu::get_item_icon);
ObjectTypeDB::bind_method(_MD("get_item_metadata","idx"),&PopupMenu::get_item_metadata);
ObjectTypeDB::bind_method(_MD("get_item_accelerator","idx"),&PopupMenu::get_item_accelerator);
+ ObjectTypeDB::bind_method(_MD("get_item_shortcut:ShortCut","idx"),&PopupMenu::get_item_shortcut);
ObjectTypeDB::bind_method(_MD("get_item_submenu","idx"),&PopupMenu::get_item_submenu);
ObjectTypeDB::bind_method(_MD("is_item_separator","idx"),&PopupMenu::is_item_separator);
ObjectTypeDB::bind_method(_MD("is_item_checkable","idx"),&PopupMenu::is_item_checkable);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 72f8795067..f35e91d4e4 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -34,6 +34,9 @@
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
+
+
+
class PopupMenu : public Popup {
OBJ_TYPE(PopupMenu, Popup );
@@ -51,6 +54,7 @@ class PopupMenu : public Popup {
String tooltip;
uint32_t accel;
int _ofs_cache;
+ Ref<ShortCut> shortcut;
Item() { checked=false; checkable=false; separator=false; accel=0; disabled=false; _ofs_cache=0; }
};
@@ -62,7 +66,7 @@ class PopupMenu : public Popup {
int mouse_over;
int submenu_over;
Rect2 parent_rect;
- String _get_accel_text(uint32_t p_accel) const;
+ String _get_accel_text(int p_item) const;
int _get_mouse_over(const Point2& p_over) const;
virtual Size2 get_minimum_size() const;
void _input_event(const InputEvent &p_event);
@@ -75,6 +79,10 @@ class PopupMenu : public Popup {
Array _get_items() const;
void _set_items(const Array& p_items);
+ Map< Ref<ShortCut>, int> shortcut_refcount;
+
+ void _ref_shortcut(Ref<ShortCut> p_sc);
+ void _unref_shortcut( Ref<ShortCut> p_sc);
protected:
virtual bool has_point(const Point2& p_point) const;
@@ -90,6 +98,11 @@ public:
void add_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);
+ void add_shortcut(const Ref<ShortCut>& p_shortcut,int p_ID=-1);
+ void add_icon_check_shortcut(const Ref<Texture>& p_icon,const Ref<ShortCut>& p_shortcut,int p_ID=-1);
+ void add_check_shortcut(const Ref<ShortCut>& p_shortcut,int p_ID=-1);
+
void set_item_text(int p_idx,const String& p_text);
void set_item_icon(int p_idx,const Ref<Texture>& p_icon);
void set_item_checked(int p_idx,bool p_checked);
@@ -101,6 +114,7 @@ public:
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_tooltip(int p_idx,const String& p_tooltip);
+ void set_item_shortcut(int p_idx, const Ref<ShortCut>& p_shortcut);
String get_item_text(int p_idx) const;
Ref<Texture> get_item_icon(int p_idx) const;
@@ -114,10 +128,11 @@ public:
bool is_item_separator(int p_idx) const;
bool is_item_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_count() const;
- int find_item_by_accelerator(uint32_t p_accel) const;
+ bool activate_item_by_event(const InputEvent& p_event);
void activate_item(int p_item);
void remove_item(int p_idx);
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index fc0e7be34f..02da8ff27e 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -35,7 +35,9 @@ Size2 ProgressBar::get_minimum_size() const {
Ref<Font> font = get_font("font");
Size2 ms=bg->get_minimum_size()+bg->get_center_size();
- ms.height=MAX(ms.height,bg->get_minimum_size().height+font->get_height());
+ if (percent_visible) {
+ ms.height=MAX(ms.height,bg->get_minimum_size().height+font->get_height());
+ }
return ms;
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index b00fcfe42c..e056c55f71 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -78,10 +78,6 @@ void Range::set_val(double p_val) {
if (p_val<shared->min)
p_val=shared->min;
- //avoid to set -0
- if (p_val == 0)
- p_val = 0;
-
if (shared->val==p_val)
return;
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index f66f909517..d5d14ad649 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -237,6 +237,7 @@ void Slider::_bind_methods() {
ADD_PROPERTY( PropertyInfo( Variant::INT, "tick_count", PROPERTY_HINT_RANGE,"0,4096,1"), _SCS("set_ticks"), _SCS("get_ticks") );
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "ticks_on_borders" ), _SCS("set_ticks_on_borders"), _SCS("get_ticks_on_borders") );
+ ADD_PROPERTY( PropertyInfo( Variant::INT,"focus_mode", PROPERTY_HINT_ENUM, "None,Click,All" ), _SCS("set_focus_mode"), _SCS("get_focus_mode") );
}
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 1c6a97bab8..d19e5f0d60 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -411,6 +411,10 @@ void TabContainer::_notification(int p_what) {
panel->draw(ci, Rect2( 0, top_size.height, size.width, size.height-top_size.height));
} break;
+ case NOTIFICATION_THEME_CHANGED: {
+
+ call_deferred("set_current_tab",get_current_tab()); //wait until all changed theme
+ } break;
}
}
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 9692d08882..eb060aa6b8 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -32,25 +32,23 @@
Size2 Tabs::get_minimum_size() const {
-
Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
Ref<Font> font = get_font("font");
- Size2 ms(0, MAX( tab_bg->get_minimum_size().height,tab_fg->get_minimum_size().height)+font->get_height() );
-
-// h+=MIN( get_constant("label_valign_fg"), get_constant("label_valign_bg") );
+ Size2 ms(0, MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height)+font->get_height());
for(int i=0;i<tabs.size();i++) {
Ref<Texture> tex = tabs[i].icon;
if (tex.is_valid()) {
- ms.height = MAX( ms.height, tex->get_size().height );
+ ms.height = MAX(ms.height, tex->get_size().height);
if (tabs[i].text!="")
ms.width+=get_constant("hseparation");
-
}
+
ms.width+=font->get_string_size(tabs[i].text).width;
+
if (current==i)
ms.width+=tab_fg->get_minimum_size().width;
else
@@ -58,28 +56,26 @@ Size2 Tabs::get_minimum_size() const {
if (tabs[i].right_button.is_valid()) {
Ref<Texture> rb=tabs[i].right_button;
- Size2 bms = rb->get_size();//+get_stylebox("button")->get_minimum_size();
+ Size2 bms = rb->get_size();
bms.width+=get_constant("hseparation");
-
ms.width+=bms.width;
ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height);
}
if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i==current)) {
Ref<Texture> cb=get_icon("close");
- Size2 bms = cb->get_size();//+get_stylebox("button")->get_minimum_size();
+ Size2 bms = cb->get_size();
bms.width+=get_constant("hseparation");
ms.width+=bms.width;
ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height);
}
}
- ms.width=0; //should make this optional
+ ms.width=0; //TODO: should make this optional
return ms;
}
-
void Tabs::_input_event(const InputEvent& p_event) {
if (p_event.type==InputEvent::MOUSE_MOTION) {
@@ -101,15 +97,14 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
}
-
-
+ // test hovering to display right or close button
int hover_buttons=-1;
hover=-1;
for(int i=0;i<tabs.size();i++) {
- // test hovering tab to display close button if policy says so
+ if (i<offset)
+ continue;
- // test hovering right button and close button
if (tabs[i].rb_rect.has_point(pos)) {
rb_hover=i;
cb_hover=-1;
@@ -123,11 +118,9 @@ void Tabs::_input_event(const InputEvent& p_event) {
break;
}
-
-
}
- if (hover_buttons == -1) { // no hover
+ if (hover_buttons == -1) { // no hover
rb_hover= hover_buttons;
cb_hover= hover_buttons;
}
@@ -137,8 +130,6 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
-
-
if (rb_pressing && p_event.type==InputEvent::MOUSE_BUTTON &&
!p_event.mouse_button.pressed &&
p_event.mouse_button.button_index==BUTTON_LEFT) {
@@ -152,9 +143,10 @@ void Tabs::_input_event(const InputEvent& p_event) {
update();
}
+
if (cb_pressing && p_event.type==InputEvent::MOUSE_BUTTON &&
- !p_event.mouse_button.pressed &&
- p_event.mouse_button.button_index==BUTTON_LEFT) {
+ !p_event.mouse_button.pressed &&
+ p_event.mouse_button.button_index==BUTTON_LEFT) {
if (cb_hover!=-1) {
//pressed
@@ -195,12 +187,12 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
}
-
int found=-1;
for(int i=0;i<tabs.size();i++) {
if (i<offset)
continue;
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing=true;
update();
@@ -213,10 +205,7 @@ void Tabs::_input_event(const InputEvent& p_event) {
return;
}
- int ofs=tabs[i].ofs_cache;
- int size = tabs[i].ofs_cache;
if (pos.x >=tabs[i].ofs_cache && pos.x<tabs[i].ofs_cache+tabs[i].size_cache) {
-
found=i;
break;
}
@@ -232,8 +221,8 @@ void Tabs::_input_event(const InputEvent& p_event) {
}
-void Tabs::_notification(int p_what) {
+void Tabs::_notification(int p_what) {
switch(p_what) {
@@ -259,39 +248,20 @@ void Tabs::_notification(int p_what) {
Ref<Texture> close=get_icon("close");
int h = get_size().height;
-
- int label_valign_fg = get_constant("label_valign_fg");
- int label_valign_bg = get_constant("label_valign_bg");
-
-
- int w=0;
-
+ int w = 0;
int mw = 0;
- {
-
-
- // h+=MIN( get_constant("label_valign_fg"), get_constant("label_valign_bg") );
-
- for(int i=0;i<tabs.size();i++) {
-
- int sz = get_tab_width(i);
-
- tabs[i].ofs_cache=mw;
- mw+=sz;
-
-
- }
+ for(int i=0;i<tabs.size();i++) {
+ tabs[i].ofs_cache = mw;
+ mw += get_tab_width(i);
}
-
if (tab_align==ALIGN_CENTER) {
w=(get_size().width-mw)/2;
} else if (tab_align==ALIGN_RIGHT) {
w=get_size().width-mw;
-
}
if (w<0) {
@@ -311,45 +281,13 @@ void Tabs::_notification(int p_what) {
if (i<offset)
continue;
- tabs[i].ofs_cache=w;
-
- String s = tabs[i].text;
- int lsize=0;
- int slen=font->get_string_size(s).width;
- lsize+=slen;
-
+ tabs[i].ofs_cache=w;
- Ref<Texture> icon;
- if (tabs[i].icon.is_valid()) {
- icon = tabs[i].icon;
- if (icon.is_valid()) {
- lsize+=icon->get_width();
- if (s!="")
- lsize+=get_constant("hseparation");
-
- }
- }
-
- if (tabs[i].right_button.is_valid()) {
- Ref<StyleBox> style = get_stylebox("button");
- Ref<Texture> rb=tabs[i].right_button;
-
- lsize+=get_constant("hseparation");
- //lsize+=style->get_margin(MARGIN_LEFT);
- lsize+=rb->get_width();
- //lsize+=style->get_margin(MARGIN_RIGHT);
-
- }
-
+ int lsize = get_tab_width(i);
- if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i==current)) {
-
- lsize+=get_constant("hseparation");
- //lsize+=style->get_margin(MARGIN_LEFT);
- lsize+=close->get_width();
- //lsize+=style->get_margin(MARGIN_RIGHT);
- }
+ String text = tabs[i].text;
+ int slen = font->get_string_size(text).width;
if (w+lsize > limit) {
max_drawn_tab=i-1;
@@ -360,42 +298,39 @@ void Tabs::_notification(int p_what) {
}
-
Ref<StyleBox> sb;
- int va;
Color col;
if (i==current) {
-
sb=tab_fg;
- va=label_valign_fg;
col=color_fg;
} else {
sb=tab_bg;
- va=label_valign_bg;
col=color_bg;
}
- Size2i sb_ms = sb->get_minimum_size();
- Rect2 sb_rect = Rect2( w, 0, lsize+sb_ms.width, h);
- sb->draw(ci, sb_rect );
+ Rect2 sb_rect = Rect2(w, 0, lsize, h);
+ sb->draw(ci, sb_rect);
w+=sb->get_margin(MARGIN_LEFT);
+ Size2i sb_ms = sb->get_minimum_size();
+ Ref<Texture> icon = tabs[i].icon;
if (icon.is_valid()) {
icon->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-icon->get_height())/2 ) );
- if (s!="")
+ if (text!="")
w+=icon->get_width()+get_constant("hseparation");
}
- font->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-font->get_height())/2+font->get_ascent() ), s, col );
+ font->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-font->get_height())/2+font->get_ascent() ), text, col );
w+=slen;
if (tabs[i].right_button.is_valid()) {
+
Ref<StyleBox> style = get_stylebox("button");
Ref<Texture> rb=tabs[i].right_button;
@@ -413,17 +348,12 @@ void Tabs::_notification(int p_what) {
style->draw(ci,rb_rect);
}
- w+=style->get_margin(MARGIN_LEFT);
-
- rb->draw(ci,Point2i( w,rb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
+ rb->draw(ci,Point2i( w+style->get_margin(MARGIN_LEFT), rb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
w+=rb->get_width();
- w+=style->get_margin(MARGIN_RIGHT);
tabs[i].rb_rect=rb_rect;
-
}
-
if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i==current)) {
Ref<StyleBox> style = get_stylebox("button");
@@ -443,11 +373,8 @@ void Tabs::_notification(int p_what) {
style->draw(ci,cb_rect);
}
- //w+=style->get_margin(MARGIN_LEFT);
-
- cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
+ cb->draw(ci,Point2i( w+style->get_margin(MARGIN_LEFT), cb_rect.pos.y+style->get_margin(MARGIN_TOP) ));
w+=cb->get_width();
- //w+=style->get_margin(MARGIN_RIGHT);
tabs[i].cb_rect=cb_rect;
}
@@ -462,28 +389,26 @@ void Tabs::_notification(int p_what) {
int vofs = (get_size().height-incr->get_size().height)/2;
if (offset>0)
- draw_texture(hilite_arrow==0?decr_hl:decr,Point2(limit,vofs));
+ draw_texture(hilite_arrow==0 ? decr_hl : decr, Point2(limit,vofs));
else
- draw_texture(decr,Point2(limit,vofs),Color(1,1,1,0.5));
+ draw_texture(decr,Point2(limit,vofs), Color(1,1,1,0.5));
if (missing_right)
- draw_texture(hilite_arrow==1?incr_hl:incr,Point2(limit+decr->get_size().width,vofs));
+ draw_texture(hilite_arrow==1 ? incr_hl : incr, Point2(limit+decr->get_size().width,vofs));
else
- draw_texture(incr,Point2(limit+decr->get_size().width,vofs),Color(1,1,1,0.5));
+ draw_texture(incr,Point2(limit+decr->get_size().width,vofs), Color(1,1,1,0.5));
buttons_visible=true;
} else {
buttons_visible=false;
}
-
} break;
}
}
int Tabs::get_tab_count() const {
-
return tabs.size();
}
@@ -492,11 +417,9 @@ void Tabs::set_current_tab(int p_current) {
ERR_FAIL_INDEX( p_current, get_tab_count() );
- //printf("DEBUG %p: set_current_tab to %i\n", this, p_current);
current=p_current;
_change_notify("current_tab");
- //emit_signal("tab_changed",current);
update();
}
@@ -520,9 +443,9 @@ String Tabs::get_tab_title(int p_tab) const{
ERR_FAIL_INDEX_V(p_tab,tabs.size(),"");
return tabs[p_tab].text;
-
}
+
void Tabs::set_tab_icon(int p_tab,const Ref<Texture>& p_icon){
ERR_FAIL_INDEX(p_tab,tabs.size());
@@ -531,6 +454,7 @@ void Tabs::set_tab_icon(int p_tab,const Ref<Texture>& p_icon){
minimum_size_changed();
}
+
Ref<Texture> Tabs::get_tab_icon(int p_tab) const{
ERR_FAIL_INDEX_V(p_tab,tabs.size(),Ref<Texture>());
@@ -539,7 +463,6 @@ Ref<Texture> Tabs::get_tab_icon(int p_tab) const{
}
-
void Tabs::set_tab_right_button(int p_tab,const Ref<Texture>& p_right_button){
ERR_FAIL_INDEX(p_tab,tabs.size());
@@ -589,8 +512,6 @@ void Tabs::remove_tab(int p_idx) {
if (current>=tabs.size())
current=tabs.size()-1;
- //emit_signal("tab_changed",current);
-
_ensure_no_over_offset();
}
@@ -614,19 +535,20 @@ int Tabs::get_tab_width(int p_idx) const {
Ref<StyleBox> tab_bg = get_stylebox("tab_bg");
Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
Ref<Font> font = get_font("font");
- Ref<Texture> close=get_icon("close");
+
int x=0;
Ref<Texture> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
+ x+=tex->get_width();
if (tabs[p_idx].text!="")
x+=get_constant("hseparation");
}
-
x+=font->get_string_size(tabs[p_idx].text).width;
+
if (current==p_idx)
x+=tab_fg->get_minimum_size().width;
else
@@ -634,17 +556,14 @@ int Tabs::get_tab_width(int p_idx) const {
if (tabs[p_idx].right_button.is_valid()) {
Ref<Texture> rb=tabs[p_idx].right_button;
- Size2 bms = rb->get_size();//+get_stylebox("button")->get_minimum_size();
- bms.width+=get_constant("hseparation");
-
- x+=bms.width;
+ x+=rb->get_width();
+ x+=get_constant("hseparation");
}
if (cb_displaypolicy==CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy==CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx==current)) {
-
- Size2 bms = close->get_size();//+get_stylebox("button")->get_minimum_size();
- bms.width+=get_constant("hseparation");
- x+=bms.width;
+ Ref<Texture> cb=get_icon("close");
+ x+=cb->get_width();
+ x+=get_constant("hseparation");
}
return x;
@@ -700,11 +619,9 @@ void Tabs::ensure_tab_visible(int p_idx) {
Ref<Texture> incr = get_icon("increment");
Ref<Texture> decr = get_icon("decrement");
-
int limit=get_size().width-incr->get_width()-decr->get_width();
-
int x=0;
for(int i=0;i<tabs.size();i++) {
@@ -750,7 +667,6 @@ void Tabs::_bind_methods() {
ADD_SIGNAL(MethodInfo("right_button_pressed",PropertyInfo(Variant::INT,"tab")));
ADD_SIGNAL(MethodInfo("tab_close",PropertyInfo(Variant::INT,"tab")));
-
ADD_PROPERTY( PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE,"-1,4096,1",PROPERTY_USAGE_EDITOR), _SCS("set_current_tab"), _SCS("get_current_tab") );
BIND_CONSTANT( ALIGN_LEFT );
@@ -774,9 +690,8 @@ Tabs::Tabs() {
cb_hover=-1;
cb_pressing=false;
- cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER; // Default : no close button
+ cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
offset=0;
max_drawn_tab=0;
-
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 0fc8e39fef..fe03b34105 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -44,7 +44,20 @@ static bool _is_text_char(CharType c) {
static bool _is_symbol(CharType c) {
- return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t');
+ return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t' || c==' ');
+}
+
+static bool _is_char(CharType c) {
+
+ return (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_';
+}
+
+static bool _is_number(CharType c) {
+ return (c >= '0' && c <= '9');
+}
+
+static bool _is_hex_symbol(CharType c) {
+ return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
}
static bool _is_pair_right_symbol(CharType c) {
@@ -301,6 +314,10 @@ void TextEdit::_update_scrollbars() {
if (line_numbers)
total_width += cache.line_number_w;
+ if (draw_breakpoint_gutter) {
+ total_width += cache.breakpoint_gutter_width;
+ }
+
bool use_hscroll=true;
bool use_vscroll=true;
@@ -399,9 +416,15 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
- };
+ } break;
case NOTIFICATION_DRAW: {
+ if (draw_breakpoint_gutter) {
+ cache.breakpoint_gutter_width = breakpoint_gutter_width;
+ } else {
+ cache.breakpoint_gutter_width = 0;
+ }
+
int line_number_char_count=0;
{
@@ -426,7 +449,7 @@ void TextEdit::_notification(int p_what) {
RID ci = get_canvas_item();
- int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w;
+ int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w+cache.breakpoint_gutter_width;
int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT);
//let's do it easy for now:
cache.style_normal->draw(ci,Rect2(Point2(),cache.size));
@@ -659,25 +682,23 @@ void TextEdit::_notification(int p_what) {
int char_ofs=0;
int ofs_y=i*get_row_height()+cache.line_spacing/2;
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;
// check if line contains highlighted word
int highlighted_text_col = -1;
- if (highlighted_text.length() != 0) {
- highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, 0);
- }
+ int search_text_col = -1;
- if (cache.line_number_w) {
- Color fcol = cache.font_color;
- fcol.a*=0.4;
- String fc = String::num(line+1);
- while (fc.length() < line_number_char_count) {
- fc="0"+fc;
- }
+ if (!search_text.empty())
+ search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
- cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol);
- }
+ 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);
@@ -692,11 +713,28 @@ void TextEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color);
}
-
if (line==cursor.line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(0, ofs_y,xmargin_end,get_row_height()),cache.current_line_color);
+ }
+
+ // draw breakpoint marker
+ if (text.is_breakpoint(line)) {
+ if (draw_breakpoint_gutter) {
+ int vertical_gap = cache.breakpoint_gutter_width / 2;
+ int marker_size = cache.breakpoint_gutter_width - vertical_gap;
+ // no transparency on marker
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + 1, ofs_y + vertical_gap ,marker_size, marker_size),Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
+ }
+ }
+
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color);
+ if (cache.line_number_w) {
+ String fc = String::num(line+1);
+ while (fc.length() < line_number_char_count) {
+ fc="0"+fc;
+ }
+ cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT)+cache.breakpoint_gutter_width,ofs_y+cache.font->get_ascent()),fc,cache.line_number_color);
}
for (int j=0;j<str.length();j++) {
@@ -712,13 +750,43 @@ void TextEdit::_notification(int p_what) {
color = cache.font_color; //reset
//find keyword
- bool is_char = _is_text_char(str[j]);
- bool is_symbol=_is_symbol(str[j]);
+ 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 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+
+ if (str[j] == 'x' && str[j-1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j])) {
+ 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)) {
@@ -760,15 +828,49 @@ void TextEdit::_notification(int p_what) {
}
}
+ 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++;
+ }
+
+ 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=symbol_color;
+ else if (is_number)
+ color=cache.number_color;
prev_is_char=is_char;
+ prev_is_number=is_number;
}
int char_w;
@@ -799,20 +901,45 @@ void TextEdit::_notification(int p_what) {
break;
}
- bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column));
+ bool in_search_result=false;
+ if (search_text_col != -1) {
+ // if we are at the end check for new search result on same line
+ if (j >= search_text_col+search_text.length())
+ search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
+
+ in_search_result = j >= search_text_col && j < search_text_col+search_text.length();
+
+ if (in_search_result) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w, get_row_height())),cache.search_result_color);
+ }
+ }
+
+ bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column));
if (in_selection) {
//inside selection!
VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color);
}
+ if (in_search_result) {
+ Color border_color=(line==search_result_line && j>=search_result_col && j<search_result_col+search_text.length())?cache.font_color:cache.search_result_border_color;
+
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,1)),border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y+get_row_height()-1 ), Size2i(char_w,1)),border_color);
+
+ if (j==search_text_col)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(1,get_row_height())),border_color);
+ if (j==search_text_col+search_text.length()-1)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin+char_w-1, ofs_y ), Size2i(1,get_row_height())),border_color);
+ }
+
if (highlight_all_occurrences) {
if (highlighted_text_col != -1) {
// if we are at the end check for new word on same line
if (j > highlighted_text_col+highlighted_text.length()) {
- highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, j);
+ highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE|SEARCH_WHOLE_WORDS, j);
}
bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col+highlighted_text.length());
@@ -857,9 +984,18 @@ void TextEdit::_notification(int p_what) {
if (cursor.column==j && cursor.line==line) {
cursor_pos = Point2i( char_ofs+char_margin, ofs_y );
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color);
+ if (insert_mode) {
+ cursor_pos.y += get_row_height();
+ }
+ if (draw_caret) {
+ if (insert_mode) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(char_w,1)),cache.caret_color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.caret_color);
+ }
+ }
}
char_ofs+=char_w;
@@ -868,8 +1004,19 @@ void TextEdit::_notification(int p_what) {
if (cursor.column==str.length() && cursor.line==line && (char_ofs+char_margin)>=xmargin_beg) {
cursor_pos=Point2i( char_ofs+char_margin, ofs_y );
- VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color);
+ if (insert_mode) {
+ cursor_pos.y += get_row_height();
+ }
+
+ if (draw_caret) {
+ if (insert_mode) {
+ int char_w = cache.font->get_char_size(' ').width;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(char_w,1)),cache.caret_color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.caret_color);
+ }
+ }
}
}
@@ -1071,7 +1218,7 @@ void TextEdit::_consume_pair_symbol(CharType ch) {
int new_column,new_line;
- _begin_compex_operation();
+ begin_complex_operation();
_insert_text(get_selection_from_line(), get_selection_from_column(),
ch_single,
&new_line, &new_column);
@@ -1084,7 +1231,7 @@ void TextEdit::_consume_pair_symbol(CharType ch) {
get_selection_to_column() + to_col_offset,
ch_single_pair,
&new_line,&new_column);
- _end_compex_operation();
+ end_complex_operation();
cursor_set_line(get_selection_to_line());
cursor_set_column(get_selection_to_column() + to_col_offset);
@@ -1168,6 +1315,66 @@ void TextEdit::backspace_at_cursor() {
}
+void TextEdit::indent_selection_right() {
+
+ if (!is_selection_active()) {
+ return;
+ }
+ begin_complex_operation();
+ int start_line = get_selection_from_line();
+ int end_line = get_selection_to_line();
+
+ // ignore if the cursor is not past the first column
+ if (get_selection_to_column() == 0) {
+ end_line--;
+ }
+
+ for (int i = start_line; i <= end_line; i++) {
+ String line_text = get_line(i);
+ line_text = '\t' + line_text;
+ set_line(i, line_text);
+ }
+
+ // fix selection being off by one on the last line
+ selection.to_column++;
+ end_complex_operation();
+ update();
+}
+
+void TextEdit::indent_selection_left() {
+
+ if (!is_selection_active()) {
+ return;
+ }
+ begin_complex_operation();
+ int start_line = get_selection_from_line();
+ int end_line = get_selection_to_line();
+
+ // ignore if the cursor is not past the first column
+ if (get_selection_to_column() == 0) {
+ end_line--;
+ }
+ String last_line_text = get_line(end_line);
+
+ for (int i = start_line; i <= end_line; i++) {
+ String line_text = get_line(i);
+
+ if (line_text.begins_with("\t")) {
+ line_text = line_text.substr(1, line_text.length());
+ set_line(i, line_text);
+ } else if (line_text.begins_with(" ")) {
+ line_text = line_text.substr(4, line_text.length());
+ set_line(i, line_text);
+ }
+ }
+
+ // fix selection being off by one on the last line
+ if (last_line_text != get_line(end_line) && selection.to_column > 0) {
+ selection.to_column--;
+ }
+ end_complex_operation();
+ update();
+}
void TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const {
@@ -1187,7 +1394,7 @@ void TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) co
col=text[row].size();
} else {
- col=p_mouse.x-(cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w);
+ col=p_mouse.x-(cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w+cache.breakpoint_gutter_width);
col+=cursor.x_ofs;
col=get_char_pos_for( col, get_line(row) );
}
@@ -1256,9 +1463,20 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
}
if (mb.button_index==BUTTON_LEFT) {
+ _reset_caret_blink_timer();
+
int row,col;
_get_mouse_pos(Point2i(mb.x,mb.y), row,col);
+ // toggle breakpoint on gutter click
+ if (draw_breakpoint_gutter) {
+ int gutter=cache.style_normal->get_margin(MARGIN_LEFT);
+ if (mb.x > gutter && mb.x <= gutter + cache.breakpoint_gutter_width + 3) {
+ set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row));
+ return;
+ }
+ }
+
int prev_col=cursor.column;
int prev_line=cursor.line;
@@ -1372,6 +1590,15 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
update();
}
+
+ if (mb.button_index==BUTTON_RIGHT) {
+
+ menu->set_pos(get_global_transform().xform(get_local_mouse_pos()));
+ menu->set_size(Vector2(1,1));
+ menu->popup();
+ grab_focus();
+
+ }
} else {
if (mb.button_index==BUTTON_LEFT)
@@ -1390,6 +1617,8 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (selection.selecting_mode!=Selection::MODE_NONE) {
+ _reset_caret_blink_timer();
+
int row,col;
_get_mouse_pos(Point2i(mm.x,mm.y), row,col);
@@ -1510,6 +1739,8 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (k.scancode==KEY_BACKSPACE) {
+ _reset_caret_blink_timer();
+
backspace_at_cursor();
_update_completion_candidates();
accept_event();
@@ -1525,20 +1756,29 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (k.unicode>32) {
- if (cursor.column<text[cursor.line].length() && text[cursor.line][cursor.column]==k.unicode) {
- //same char, move ahead
- cursor_set_column(cursor.column+1);
+ _reset_caret_blink_timer();
+ const CharType chr[2] = {(CharType)k.unicode, 0};
+ if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
+ _consume_pair_symbol(chr[0]);
} else {
- //different char, go back
- const CharType chr[2] = {(CharType)k.unicode, 0};
- if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
- _consume_pair_symbol(chr[0]);
- } else {
- _insert_text_at_cursor(chr);
+
+ // remove the old character if in insert mode
+ if (insert_mode) {
+ begin_complex_operation();
+
+ // make sure we don't try and remove empty space
+ if (cursor.column < get_line(cursor.line).length()) {
+ _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
+ }
}
- }
+ _insert_text_at_cursor(chr);
+
+ if (insert_mode) {
+ end_complex_operation();
+ }
+ }
_update_completion_candidates();
accept_event();
@@ -1564,8 +1804,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
k.mod.shift=false;
}
- // stuff to do when selection is active..
+ if (!k.mod.command) {
+ _reset_caret_blink_timer();
+ }
+ // save here for insert mode, just in case it is cleared in the following section
+ bool had_selection = selection.active;
+ // stuff to do when selection is active..
if (selection.active) {
if (readonly)
@@ -1578,51 +1823,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
switch(k.scancode) {
case KEY_TAB: {
-
- String txt = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- String prev_txt=txt;
-
if (k.mod.shift) {
-
- for(int i=0;i<txt.length();i++) {
- if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/)) && (txt[i]=='\t' || txt[i]==' ')) {
- txt.remove(i);
- //i--;
- }
- }
+ indent_selection_left();
} else {
-
- for(int i=0;i<txt.length();i++) {
-
- if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/))) {
- txt=txt.insert(i,"\t");
- //i--;
- }
- }
+ indent_selection_right();
}
-
- if (txt!=prev_txt) {
-
- int sel_line=selection.from_line;
- int sel_column=selection.from_column;
-
- cursor_set_line(selection.from_line);
- cursor_set_column(selection.from_column);
- _begin_compex_operation();
- _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- _insert_text_at_cursor(txt);
- _end_compex_operation();
- selection.active=true;
- selection.from_column=sel_column;
- selection.from_line=sel_line;
- selection.to_column=cursor.column;
- selection.to_line=cursor.line;
- update();
- }
-
dobreak=true;
accept_event();
-
} break;
case KEY_X:
case KEY_C:
@@ -1630,6 +1837,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
clear=(!k.mod.command || k.mod.shift || k.mod.alt );
break;
case KEY_DELETE:
+ if (!k.mod.shift) {
+ accept_event();
+ clear=true; dobreak=true;
+ } else if (k.mod.command || k.mod.alt) {
+ dobreak=true;
+ }
+ break;
case KEY_BACKSPACE:
accept_event();
clear=true; dobreak=true;
@@ -1662,6 +1876,9 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
}
if (clear) {
+ if (!dobreak) {
+ begin_complex_operation();
+ }
selection.active=false;
update();
_remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
@@ -1733,7 +1950,8 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (completion_hint!="") {
completion_hint="";
update();
-
+ } else {
+ scancode_handled=false;
}
} break;
case KEY_TAB: {
@@ -1936,7 +2154,17 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
scancode_handled=false;
break;
}
-#ifdef APPLE_STYLE_KEYS
+#ifndef APPLE_STYLE_KEYS
+ if (k.mod.command) {
+ _scroll_lines_up();
+ break;
+ }
+#else
+ if (k.mod.command && k.mod.alt) {
+ _scroll_lines_up();
+ break;
+ }
+
if (k.mod.command)
cursor_set_line(0);
else
@@ -1963,7 +2191,17 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
scancode_handled=false;
break;
}
-#ifdef APPLE_STYLE_KEYS
+#ifndef APPLE_STYLE_KEYS
+ if (k.mod.command) {
+ _scroll_lines_down();
+ break;
+ }
+#else
+ if (k.mod.command && k.mod.alt) {
+ _scroll_lines_down();
+ break;
+ }
+
if (k.mod.command)
cursor_set_line(text.size()-1);
else
@@ -2295,6 +2533,12 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
}
}
*/
+ if (k.scancode==KEY_INSERT) {
+ set_insert_mode(!insert_mode);
+ accept_event();
+ return;
+ }
+
if (!scancode_handled && !k.mod.command) { //for german kbds
if (k.unicode>=32) {
@@ -2302,6 +2546,16 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
if (readonly)
break;
+ // remove the old character if in insert mode and no selection
+ if (insert_mode && !had_selection) {
+ begin_complex_operation();
+
+ // make sure we don't try and remove empty space
+ if (cursor.column < get_line(cursor.line).length()) {
+ _remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
+ }
+ }
+
const CharType chr[2] = {(CharType)k.unicode, 0};
if (completion_hint!="" && k.unicode==')') {
@@ -2313,6 +2567,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
_insert_text_at_cursor(chr);
}
+ if (insert_mode && !had_selection) {
+ end_complex_operation();
+ }
+
+ if (selection.active != had_selection) {
+ end_complex_operation();
+ }
accept_event();
} else {
@@ -2354,6 +2615,36 @@ void TextEdit::_post_shift_selection() {
selection.selecting_text=true;
}
+void TextEdit::_scroll_lines_up() {
+ // adjust the vertical scroll
+ if (get_v_scroll() > 0) {
+ set_v_scroll(get_v_scroll() - 1);
+ }
+
+ // adjust the cursor
+ if (cursor_get_line() >= (get_visible_rows() + get_v_scroll()) && !selection.active) {
+ cursor_set_line((get_visible_rows() + get_v_scroll()) - 1, false);
+ }
+}
+
+void TextEdit::_scroll_lines_down() {
+ // calculate the maximum vertical scroll position
+ int max_v_scroll = get_line_count() - 1;
+ if (!scroll_past_end_of_file_enabled) {
+ max_v_scroll -= get_visible_rows() - 1;
+ }
+
+ // adjust the vertical scroll
+ if (get_v_scroll() < max_v_scroll) {
+ set_v_scroll(get_v_scroll() + 1);
+ }
+
+ // adjust the cursor
+ if ((cursor_get_line()) <= get_v_scroll() - 1 && !selection.active) {
+ cursor_set_line(get_v_scroll(), false);
+ }
+}
+
/**** TEXT EDIT CORE API ****/
void TextEdit::_base_insert_text(int p_line, int p_char,const String& p_text,int &r_end_line,int &r_end_column) {
@@ -2614,7 +2905,7 @@ void TextEdit::adjust_viewport_to_cursor() {
if (cursor.line_ofs>cursor.line)
cursor.line_ofs=cursor.line;
- int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w;
+ int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w-cache.breakpoint_gutter_width;
if (v_scroll->is_visible())
visible_width-=v_scroll->get_combined_minimum_size().width;
visible_width-=20; // give it a little more space
@@ -2716,6 +3007,30 @@ int TextEdit::cursor_get_line() const {
return cursor.line;
}
+bool TextEdit::cursor_get_blink_enabled() const {
+ return caret_blink_enabled;
+}
+
+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();
+ }
+ draw_caret = true;
+}
+
+
+float TextEdit::cursor_get_blink_speed() const {
+ return caret_blink_timer->get_wait_time();
+}
+
+void TextEdit::cursor_set_blink_speed(const float p_speed) {
+ ERR_FAIL_COND(p_speed <= 0);
+ caret_blink_timer->set_wait_time(p_speed);
+}
void TextEdit::_scroll_moved(double p_to_val) {
@@ -2819,7 +3134,8 @@ void TextEdit::insert_text_at_cursor(const String& p_text) {
}
Control::CursorShape TextEdit::get_cursor_shape(const Point2& p_pos) const {
- if(completion_active && completion_rect.has_point(p_pos)) {
+ int gutter=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w+cache.breakpoint_gutter_width;
+ if((completion_active && completion_rect.has_point(p_pos)) || p_pos.x < gutter) {
return CURSOR_ARROW;
}
return CURSOR_IBEAM;
@@ -2931,20 +3247,41 @@ void TextEdit::set_max_chars(int p_max_chars) {
max_chars=p_max_chars;
}
+void TextEdit::_reset_caret_blink_timer() {
+ if (caret_blink_enabled) {
+ caret_blink_timer->stop();
+ caret_blink_timer->start();
+ draw_caret = true;
+ update();
+ }
+}
+
+void TextEdit::_toggle_draw_caret() {
+ draw_caret = !draw_caret;
+ update();
+}
+
void TextEdit::_update_caches() {
cache.style_normal=get_stylebox("normal");
cache.style_focus=get_stylebox("focus");
cache.font=get_font("font");
+ cache.caret_color=get_color("caret_color");
+ cache.line_number_color=get_color("line_number_color");
cache.font_color=get_color("font_color");
cache.font_selected_color=get_color("font_selected_color");
cache.keyword_color=get_color("keyword_color");
+ cache.function_color=get_color("function_color");
+ cache.member_variable_color=get_color("member_variable_color");
+ cache.number_color=get_color("number_color");
cache.selection_color=get_color("selection_color");
cache.mark_color=get_color("mark_color");
cache.current_line_color=get_color("current_line_color");
cache.breakpoint_color=get_color("breakpoint_color");
cache.brace_mismatch_color=get_color("brace_mismatch_color");
cache.word_highlighted_color=get_color("word_highlighted_color");
+ cache.search_result_color=get_color("search_result_color");
+ cache.search_result_border_color=get_color("search_result_border_color");
cache.line_spacing=get_constant("line_spacing");
cache.row_height = cache.font->get_height() + cache.line_spacing;
cache.tab_icon=get_icon("tab");
@@ -3206,12 +3543,26 @@ String TextEdit::get_word_under_cursor() const {
return text[cursor.line].substr(prev_cc, next_cc-prev_cc);
}
+void TextEdit::set_search_text(const String &p_search_text) {
+ search_text = p_search_text;
+}
+
+void TextEdit::set_search_flags(uint32_t p_flags) {
+ search_flags = p_flags;
+}
+
+void TextEdit::set_current_search_result(int line, int col) {
+ search_result_line = line;
+ search_result_col = col;
+ update();
+}
+
void TextEdit::set_highlight_all_occurrences(const bool p_enabled) {
highlight_all_occurrences = p_enabled;
update();
}
-int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_search, int p_from_column) {
+int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) {
int col = -1;
if (p_key.length() > 0 && p_search.length() > 0) {
@@ -3219,12 +3570,15 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
p_from_column = 0;
}
- while (col == -1 && p_from_column <= p_search.length()) {
- // match case
- col = p_search.findn(p_key, p_from_column);
+ while (col == -1 && p_from_column <= p_search.length()) {
+ if (p_search_flags&SEARCH_MATCH_CASE) {
+ col = p_search.find(p_key,p_from_column);
+ } else {
+ col = p_search.findn(p_key,p_from_column);
+ }
// whole words only
- if (col != -1) {
+ if (col != -1 && p_search_flags&SEARCH_WHOLE_WORDS) {
p_from_column=col;
if (col > 0 && _is_text_char(p_search[col-1])) {
@@ -3290,10 +3644,8 @@ bool TextEdit::search(const String &p_key,uint32_t p_search_flags, int p_from_li
//wrapped
if (p_search_flags&SEARCH_BACKWARDS) {
- text_line=text_line.substr(from_column,text_line.length());
from_column=text_line.length();
} else {
- text_line=text_line.substr(0,from_column);
from_column=0;
}
@@ -3304,7 +3656,6 @@ bool TextEdit::search(const String &p_key,uint32_t p_search_flags, int p_from_li
} else {
- //text_line=text_line.substr(0,p_from_column); //wrap around for missing begining.
if (p_search_flags&SEARCH_BACKWARDS)
from_column=text_line.length()-1;
else
@@ -3313,12 +3664,25 @@ bool TextEdit::search(const String &p_key,uint32_t p_search_flags, int p_from_li
pos=-1;
- if (!(p_search_flags&SEARCH_BACKWARDS)) {
+ int pos_from=0;
+ int last_pos=-1;
+ while ((last_pos=(p_search_flags&SEARCH_MATCH_CASE)?text_line.find(p_key,pos_from):text_line.findn(p_key,pos_from))!=-1) {
- pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.find(p_key,from_column):text_line.findn(p_key,from_column);
- } else {
+ if (p_search_flags&SEARCH_BACKWARDS) {
+
+ if (last_pos>from_column)
+ break;
+ pos=last_pos;
+
+ } else {
+
+ if (last_pos>=from_column) {
+ pos=last_pos;
+ break;
+ }
+ }
- pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.rfind(p_key,from_column):text_line.rfindn(p_key,from_column);
+ pos_from=last_pos+p_key.length();
}
if (pos!=-1 && (p_search_flags&SEARCH_WHOLE_WORDS)) {
@@ -3502,12 +3866,12 @@ void TextEdit::clear_undo_history() {
}
-void TextEdit::_begin_compex_operation() {
+void TextEdit::begin_complex_operation() {
_push_current_op();
next_operation_is_complex=true;
}
-void TextEdit::_end_compex_operation() {
+void TextEdit::end_complex_operation() {
_push_current_op();
ERR_FAIL_COND(undo_stack.size() == 0);
@@ -3554,6 +3918,15 @@ bool TextEdit::is_drawing_tabs() const{
return draw_tabs;
}
+void TextEdit::set_insert_mode(bool p_enabled) {
+ insert_mode = p_enabled;
+ update();
+}
+
+bool TextEdit::is_insert_mode() const {
+ return insert_mode;
+}
+
uint32_t TextEdit::get_version() const {
return current_op.version;
}
@@ -3703,6 +4076,9 @@ void TextEdit::_update_completion_candidates() {
}
}
+ if (l[cursor.column - 1] == '(' && !pre_keyword && !completion_strings[0].begins_with("\"")) {
+ cancel = true;
+ }
update();
@@ -3718,6 +4094,10 @@ void TextEdit::_update_completion_candidates() {
int ci_match=0;
for(int i=0;i<completion_strings.size();i++) {
if (completion_strings[i].begins_with(s)) {
+ // don't remove duplicates if no input is provided
+ if (completion_options.find(completion_strings[i]) != -1 && s != "") {
+ continue;
+ }
completion_options.push_back(completion_strings[i]);
int m=0;
int max=MIN(completion_current.length(),completion_strings[i].length());
@@ -3876,10 +4256,60 @@ void TextEdit::set_show_line_numbers(bool p_show) {
update();
}
+void TextEdit::set_draw_breakpoint_gutter(bool p_draw) {
+ draw_breakpoint_gutter = p_draw;
+ update();
+}
+
+bool TextEdit::is_drawing_breakpoint_gutter() const {
+ return draw_breakpoint_gutter;
+}
+
+void TextEdit::set_breakpoint_gutter_width(int p_gutter_width) {
+ breakpoint_gutter_width = p_gutter_width;
+ update();
+}
+
+int TextEdit::get_breakpoint_gutter_width() const {
+ return cache.breakpoint_gutter_width;
+}
+
bool TextEdit::is_text_field() const {
return true;
}
+
+void TextEdit::menu_option(int p_option) {
+
+ switch( p_option ) {
+ case MENU_CUT: {
+
+ cut();
+ } break;
+ case MENU_COPY: {
+ copy();
+ } break;
+ case MENU_PASTE: {
+
+ paste();
+ } break;
+ case MENU_CLEAR: {
+ clear();
+ } break;
+ case MENU_SELECT_ALL: {
+ select_all();
+ } break;
+ case MENU_UNDO: {
+ undo();
+ } break;
+
+ };
+}
+
+PopupMenu *TextEdit::get_menu() const {
+ return menu;
+}
+
void TextEdit::_bind_methods() {
@@ -3889,6 +4319,7 @@ void TextEdit::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_text_changed_emit"),&TextEdit::_text_changed_emit);
ObjectTypeDB::bind_method(_MD("_push_current_op"),&TextEdit::_push_current_op);
ObjectTypeDB::bind_method(_MD("_click_selection_held"),&TextEdit::_click_selection_held);
+ ObjectTypeDB::bind_method(_MD("_toggle_draw_caret"),&TextEdit::_toggle_draw_caret);
BIND_CONSTANT( SEARCH_MATCH_CASE );
BIND_CONSTANT( SEARCH_WHOLE_WORDS );
@@ -3911,7 +4342,10 @@ void TextEdit::_bind_methods() {
ObjectTypeDB::bind_method(_MD("cursor_get_column"),&TextEdit::cursor_get_column);
ObjectTypeDB::bind_method(_MD("cursor_get_line"),&TextEdit::cursor_get_line);
-
+ ObjectTypeDB::bind_method(_MD("cursor_set_blink_enabled", "enable"),&TextEdit::cursor_set_blink_enabled);
+ ObjectTypeDB::bind_method(_MD("cursor_get_blink_enabled"),&TextEdit::cursor_get_blink_enabled);
+ ObjectTypeDB::bind_method(_MD("cursor_set_blink_speed", "blink_speed"),&TextEdit::cursor_set_blink_speed);
+ ObjectTypeDB::bind_method(_MD("cursor_get_blink_speed"),&TextEdit::cursor_get_blink_speed);
ObjectTypeDB::bind_method(_MD("set_readonly","enable"),&TextEdit::set_readonly);
ObjectTypeDB::bind_method(_MD("set_wrap","enable"),&TextEdit::set_wrap);
@@ -3945,12 +4379,25 @@ void TextEdit::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_symbol_color","color"),&TextEdit::set_symbol_color);
ObjectTypeDB::bind_method(_MD("set_custom_bg_color","color"),&TextEdit::set_custom_bg_color);
ObjectTypeDB::bind_method(_MD("clear_colors"),&TextEdit::clear_colors);
+ ObjectTypeDB::bind_method(_MD("menu_option"),&TextEdit::menu_option);
+ ObjectTypeDB::bind_method(_MD("get_menu:PopupMenu"),&TextEdit::get_menu);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret/caret_blink"), _SCS("cursor_set_blink_enabled"), _SCS("cursor_get_blink_enabled"));;
+ ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret/caret_blink_speed",PROPERTY_HINT_RANGE,"0.1,10,0.1"), _SCS("cursor_set_blink_speed"),_SCS("cursor_get_blink_speed") );
ADD_SIGNAL(MethodInfo("cursor_changed"));
ADD_SIGNAL(MethodInfo("text_changed"));
ADD_SIGNAL(MethodInfo("request_completion"));
+ BIND_CONSTANT( MENU_CUT );
+ BIND_CONSTANT( MENU_COPY );
+ BIND_CONSTANT( MENU_PASTE );
+ BIND_CONSTANT( MENU_CLEAR );
+ BIND_CONSTANT( MENU_SELECT_ALL );
+ BIND_CONSTANT( MENU_UNDO );
+ BIND_CONSTANT( MENU_MAX );
+
+
}
TextEdit::TextEdit() {
@@ -3958,6 +4405,7 @@ TextEdit::TextEdit() {
readonly=false;
setting_row=false;
draw_tabs=false;
+ draw_caret=true;
max_chars=0;
clear();
wrap=false;
@@ -3967,6 +4415,8 @@ TextEdit::TextEdit() {
cache.row_height=1;
cache.line_spacing=1;
cache.line_number_w=1;
+ cache.breakpoint_gutter_width=0;
+ breakpoint_gutter_width = 0;
tab_size=4;
text.set_tab_size(tab_size);
@@ -3997,6 +4447,13 @@ TextEdit::TextEdit() {
selection.active=false;
syntax_coloring=false;
+ caret_blink_enabled=false;
+ caret_blink_timer = memnew(Timer);
+ add_child(caret_blink_timer);
+ caret_blink_timer->set_wait_time(0.65);
+ caret_blink_timer->connect("timeout", this,"_toggle_draw_caret");
+ cursor_set_blink_enabled(false);
+
custom_bg_color=Color(0,0,0,0);
idle_detect = memnew( Timer );
add_child(idle_detect);
@@ -4041,11 +4498,27 @@ TextEdit::TextEdit() {
completion_line_ofs=0;
tooltip_obj=NULL;
line_numbers=false;
+ draw_breakpoint_gutter=false;
next_operation_is_complex=false;
scroll_past_end_of_file_enabled=false;
auto_brace_completion_enabled=false;
brace_matching_enabled=false;
auto_indent=false;
+ insert_mode = false;
+
+ menu = memnew( PopupMenu );
+ add_child(menu);
+ menu->add_item(TTR("Cut"),MENU_CUT,KEY_MASK_CMD|KEY_X);
+ menu->add_item(TTR("Copy"),MENU_COPY,KEY_MASK_CMD|KEY_C);
+ menu->add_item(TTR("Paste"),MENU_PASTE,KEY_MASK_CMD|KEY_V);
+ menu->add_separator();
+ menu->add_item(TTR("Select All"),MENU_SELECT_ALL,KEY_MASK_CMD|KEY_A);
+ menu->add_item(TTR("Clear"),MENU_CLEAR);
+ menu->add_separator();
+ menu->add_item(TTR("Undo"),MENU_UNDO,KEY_MASK_CMD|KEY_Z);
+ menu->connect("item_pressed",this,"menu_option");
+
+
}
TextEdit::~TextEdit()
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 86696ca5a5..24a72afd48 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -31,6 +31,7 @@
#include "scene/gui/control.h"
#include "scene/gui/scroll_bar.h"
+#include "scene/gui/popup_menu.h"
#include "scene/main/timer.h"
@@ -73,19 +74,27 @@ class TextEdit : public Control {
Ref<StyleBox> style_normal;
Ref<StyleBox> style_focus;
Ref<Font> font;
+ Color caret_color;
+ Color line_number_color;
Color font_color;
Color font_selected_color;
Color keyword_color;
+ Color number_color;
+ Color function_color;
+ Color member_variable_color;
Color selection_color;
Color mark_color;
Color breakpoint_color;
Color current_line_color;
Color brace_mismatch_color;
Color word_highlighted_color;
+ Color search_result_color;
+ Color search_result_border_color;
int row_height;
int line_spacing;
int line_number_w;
+ int breakpoint_gutter_width;
Size2 size;
} cache;
@@ -205,6 +214,10 @@ class TextEdit : public Control {
bool syntax_coloring;
int tab_size;
+ Timer *caret_blink_timer;
+ bool caret_blink_enabled;
+ bool draw_caret;
+
bool setting_row;
bool wrap;
bool draw_tabs;
@@ -212,6 +225,8 @@ class TextEdit : public Control {
bool text_changed_dirty;
bool undo_enabled;
bool line_numbers;
+ bool draw_breakpoint_gutter;
+ int breakpoint_gutter_width;
bool highlight_all_occurrences;
bool scroll_past_end_of_file_enabled;
@@ -219,6 +234,7 @@ class TextEdit : public Control {
bool brace_matching_enabled;
bool auto_indent;
bool cut_copy_line;
+ bool insert_mode;
uint64_t last_dblclk;
@@ -238,6 +254,11 @@ class TextEdit : public Control {
bool callhint_below;
Vector2 callhint_offset;
+ String search_text;
+ uint32_t search_flags;
+ int search_result_line;
+ int search_result_col;
+
int get_visible_rows() const;
int get_char_count();
@@ -253,17 +274,21 @@ class TextEdit : public Control {
void _pre_shift_selection();
void _post_shift_selection();
+ void _scroll_lines_up();
+ void _scroll_lines_down();
+
// void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask);
Size2 get_minimum_size();
int get_row_height() const;
+ void _reset_caret_blink_timer();
+ void _toggle_draw_caret();
+
void _update_caches();
void _cursor_changed_emit();
void _text_changed_emit();
- void _begin_compex_operation();
- void _end_compex_operation();
void _push_current_op();
/* super internal api, undo/redo builds on it */
@@ -272,10 +297,12 @@ class TextEdit : public Control {
String _base_get_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) const;
void _base_remove_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column);
- int _get_column_pos_of_word(const String &p_key, const String &p_search, int p_from_column);
+ int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column);
DVector<int> _search_bind(const String &p_key,uint32_t p_search_flags, int p_from_line,int p_from_column) const;
+ PopupMenu *menu;
+
void _clear();
void _cancel_completion();
void _cancel_code_hint();
@@ -303,6 +330,17 @@ protected:
public:
+ enum MenuItems {
+ MENU_CUT,
+ MENU_COPY,
+ MENU_PASTE,
+ MENU_CLEAR,
+ MENU_SELECT_ALL,
+ MENU_UNDO,
+ MENU_MAX
+
+ };
+
enum SearchFlags {
SEARCH_MATCH_CASE=1,
@@ -315,6 +353,9 @@ public:
//void delete_char();
//void delete_line();
+ void begin_complex_operation();
+ void end_complex_operation();
+
void set_text(String p_text);
void insert_text_at_cursor(const String& p_text);
void insert_at(const String& p_text, int at);
@@ -328,6 +369,9 @@ public:
void set_line(int line, String new_text);
void backspace_at_cursor();
+ void indent_selection_left();
+ void indent_selection_right();
+
inline void set_scroll_pass_end_of_file(bool p_enabled) {
scroll_past_end_of_file_enabled = p_enabled;
update();
@@ -351,6 +395,12 @@ public:
int cursor_get_column() const;
int cursor_get_line() const;
+ bool cursor_get_blink_enabled() const;
+ void cursor_set_blink_enabled(const bool p_enabled);
+
+ float cursor_get_blink_speed() const;
+ void cursor_set_blink_speed(const float p_speed);
+
void set_readonly(bool p_readonly);
void set_max_chars(int p_max_chars);
@@ -368,6 +418,10 @@ public:
void select(int p_from_line,int p_from_column,int p_to_line,int p_to_column);
void deselect();
+ void set_search_text(const String& p_search_text);
+ void set_search_flags(uint32_t p_flags);
+ void set_current_search_result(int line, int col);
+
void set_highlight_all_occurrences(const bool p_enabled);
bool is_selection_active() const;
int get_selection_from_line() const;
@@ -388,6 +442,9 @@ public:
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
+ void set_insert_mode(bool p_enabled);
+ bool is_insert_mode() const;
+
void add_keyword_color(const String& p_keyword,const Color& p_color);
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 set_symbol_color(const Color& p_color);
@@ -404,8 +461,16 @@ public:
uint32_t get_saved_version() const;
void tag_saved_version();
+ void menu_option(int p_option);
+
void set_show_line_numbers(bool p_show);
+ void set_draw_breakpoint_gutter(bool p_draw);
+ bool is_drawing_breakpoint_gutter() const;
+
+ void set_breakpoint_gutter_width(int p_gutter_width);
+ int get_breakpoint_gutter_width() const;
+
void set_tooltip_request_func(Object *p_obj, const StringName& p_function, const Variant& p_udata);
void set_completion(bool p_enabled,const Vector<String>& p_prefixes);
@@ -413,6 +478,8 @@ public:
void set_code_hint(const String& p_hint);
void query_code_comple();
+ PopupMenu *get_menu() const;
+
String get_text_for_completion();
virtual bool is_text_field() const;
diff --git a/scene/gui/texture_frame.cpp b/scene/gui/texture_frame.cpp
index 73fecf591a..2fe8735fda 100644
--- a/scene/gui/texture_frame.cpp
+++ b/scene/gui/texture_frame.cpp
@@ -37,9 +37,31 @@ void TextureFrame::_notification(int p_what) {
return;
- Size2 s=expand?get_size():texture->get_size();
+
RID ci = get_canvas_item();
- draw_texture_rect(texture,Rect2(Point2(),s),false,modulate);
+
+ switch(stretch_mode) {
+ case STRETCH_SCALE_ON_EXPAND: {
+ Size2 s=expand?get_size():texture->get_size();
+ draw_texture_rect(texture,Rect2(Point2(),s),false,modulate);
+ } break;
+ case STRETCH_SCALE: {
+ draw_texture_rect(texture,Rect2(Point2(),get_size()),false,modulate);
+ } break;
+ case STRETCH_TILE: {
+ draw_texture_rect(texture,Rect2(Point2(),get_size()),true,modulate);
+ } break;
+ case STRETCH_KEEP: {
+ draw_texture_rect(texture,Rect2(Point2(),texture->get_size()),false,modulate);
+
+ } break;
+ case STRETCH_KEEP_CENTERED: {
+
+ Vector2 ofs = (get_size() - texture->get_size())/2;
+ draw_texture_rect(texture,Rect2(ofs,texture->get_size()),false,modulate);
+ } break;
+ }
+
/*
Vector<Point2> points;
@@ -76,11 +98,19 @@ void TextureFrame::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_modulate"), & TextureFrame::get_modulate );
ObjectTypeDB::bind_method(_MD("set_expand","enable"), & TextureFrame::set_expand );
ObjectTypeDB::bind_method(_MD("has_expand"), & TextureFrame::has_expand );
+ ObjectTypeDB::bind_method(_MD("set_stretch_mode","stretch_mode"), & TextureFrame::set_stretch_mode );
+ ObjectTypeDB::bind_method(_MD("get_stretch_mode"), & TextureFrame::get_stretch_mode );
ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"),_SCS("get_texture") );
ADD_PROPERTYNO( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate") );
ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "expand" ), _SCS("set_expand"),_SCS("has_expand") );
+ ADD_PROPERTYNO( PropertyInfo( Variant::INT, "stretch_mode",PROPERTY_HINT_ENUM,"Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered"), _SCS("set_stretch_mode"),_SCS("get_stretch_mode") );
+ BIND_CONSTANT( STRETCH_SCALE_ON_EXPAND );
+ BIND_CONSTANT( STRETCH_SCALE );
+ BIND_CONSTANT( STRETCH_TILE );
+ BIND_CONSTANT( STRETCH_KEEP );
+ BIND_CONSTANT( STRETCH_KEEP_CENTERED );
}
@@ -121,12 +151,24 @@ bool TextureFrame::has_expand() const {
return expand;
}
+void TextureFrame::set_stretch_mode(StretchMode p_mode) {
+
+ stretch_mode=p_mode;
+ update();
+}
+
+TextureFrame::StretchMode TextureFrame::get_stretch_mode() const {
+
+ return stretch_mode;
+}
+
TextureFrame::TextureFrame() {
expand=false;
modulate=Color(1,1,1,1);
set_ignore_mouse(true);
+ stretch_mode=STRETCH_SCALE_ON_EXPAND;
}
diff --git a/scene/gui/texture_frame.h b/scene/gui/texture_frame.h
index e1f0de92df..a4acf588ea 100644
--- a/scene/gui/texture_frame.h
+++ b/scene/gui/texture_frame.h
@@ -36,10 +36,19 @@
class TextureFrame : public Control {
OBJ_TYPE(TextureFrame,Control);
-
+public:
+ enum StretchMode {
+ STRETCH_SCALE_ON_EXPAND, //default, for backwards compatibility
+ STRETCH_SCALE,
+ STRETCH_TILE,
+ STRETCH_KEEP,
+ STRETCH_KEEP_CENTERED,
+ };
+private:
bool expand;
Color modulate;
Ref<Texture> texture;
+ StretchMode stretch_mode;
protected:
void _notification(int p_what);
@@ -57,9 +66,13 @@ public:
void set_expand(bool p_expand);
bool has_expand() const;
+ void set_stretch_mode(StretchMode p_mode);
+ StretchMode get_stretch_mode() const;
+
TextureFrame();
~TextureFrame();
};
+VARIANT_ENUM_CAST( TextureFrame::StretchMode );
#endif // TEXTURE_FRAME_H
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 718206dee1..2c39aea08c 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -154,7 +154,7 @@ void TreeItem::set_text(int p_column,String p_text) {
ERR_FAIL_INDEX( p_column, cells.size() );
cells[p_column].text=p_text;
- if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE) {
+ if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE || cells[p_column].mode==TreeItem::CELL_MODE_RANGE_EXPRESSION) {
cells[p_column].min=0;
cells[p_column].max=p_text.get_slice_count(",");
@@ -604,10 +604,11 @@ String TreeItem::get_tooltip(int p_column) const{
return cells[p_column].tooltip;
}
-void TreeItem::set_custom_bg_color(int p_column,const Color& p_color) {
+void TreeItem::set_custom_bg_color(int p_column,const Color& p_color,bool p_bg_outline) {
ERR_FAIL_INDEX( p_column, cells.size() );
cells[p_column].custom_bg_color=true;
+ cells[p_column].custom_bg_outline=p_bg_outline;
cells[p_column].bg_color=p_color;
_changed_notify(p_column);
}
@@ -685,7 +686,7 @@ void TreeItem::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_custom_color","column","color"),&TreeItem::set_custom_color);
ObjectTypeDB::bind_method(_MD("clear_custom_color","column"),&TreeItem::clear_custom_color);
- ObjectTypeDB::bind_method(_MD("set_custom_bg_color","column","color"),&TreeItem::set_custom_bg_color);
+ ObjectTypeDB::bind_method(_MD("set_custom_bg_color","column","color","just_outline"),&TreeItem::set_custom_bg_color,DEFVAL(false));
ObjectTypeDB::bind_method(_MD("clear_custom_bg_color","column"),&TreeItem::clear_custom_bg_color);
ObjectTypeDB::bind_method(_MD("get_custom_bg_color","column"),&TreeItem::get_custom_bg_color);
@@ -704,6 +705,7 @@ void TreeItem::_bind_methods() {
BIND_CONSTANT( CELL_MODE_STRING );
BIND_CONSTANT( CELL_MODE_CHECK );
BIND_CONSTANT( CELL_MODE_RANGE );
+ BIND_CONSTANT( CELL_MODE_RANGE_EXPRESSION );
BIND_CONSTANT( CELL_MODE_ICON );
BIND_CONSTANT( CELL_MODE_CUSTOM );
@@ -756,6 +758,13 @@ TreeItem::~TreeItem() {
if (tree && tree->selected_item==this)
tree->selected_item=NULL;
+
+ if (tree && tree->drop_mode_over==this)
+ tree->drop_mode_over=NULL;
+
+ if (tree && tree->single_select_defer==this)
+ tree->single_select_defer=NULL;
+
if (tree && tree->edited_item==this) {
tree->edited_item=NULL;
tree->pressing_for_editor=false;
@@ -796,17 +805,13 @@ void Tree::update_cache() {
cache.font_color=get_color("font_color");
cache.font_color_selected=get_color("font_color_selected");
cache.guide_color=get_color("guide_color");
+ cache.drop_position_color=get_color("drop_position_color");
cache.hseparation=get_constant("hseparation");
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");
- Ref<StyleBox> title_button;
- Ref<StyleBox> title_button_hover;
- Ref<StyleBox> title_button_pressed;
- Color title_button_color;
-
cache.title_button = get_stylebox("title_button_normal");
cache.title_button_pressed = get_stylebox("title_button_pressed");
cache.title_button_hover = get_stylebox("title_button_hover");
@@ -1086,7 +1091,34 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2&
Rect2 r=cell_rect;
r.pos.x-=cache.hseparation;
r.size.x+=cache.hseparation;
- VisualServer::get_singleton()->canvas_item_add_rect(ci,r,p_item->cells[i].bg_color);
+ if (p_item->cells[i].custom_bg_outline) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,r.size.x,1),p_item->cells[i].bg_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y+r.size.y-1,r.size.x,1),p_item->cells[i].bg_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,1,r.size.y),p_item->cells[i].bg_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x+r.size.x-1,r.pos.y,1,r.size.y),p_item->cells[i].bg_color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,r,p_item->cells[i].bg_color);
+ }
+ }
+
+ if (drop_mode_flags && drop_mode_over==p_item) {
+
+ Rect2 r=cell_rect;
+
+ if (drop_mode_section==-1 || drop_mode_section==0) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,r.size.x,1),cache.drop_position_color);
+
+ }
+
+ if (drop_mode_section==0) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y,1,r.size.y),cache.drop_position_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x+r.size.x-1,r.pos.y,1,r.size.y),cache.drop_position_color);
+
+ }
+
+ if (drop_mode_section==1 || drop_mode_section==0) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(r.pos.x,r.pos.y+r.size.y,r.size.x,1),cache.drop_position_color);
+ }
}
Color col=p_item->cells[i].custom_color?p_item->cells[i].color:get_color( p_item->cells[i].selected?"font_color_selected":"font_color");
@@ -1127,7 +1159,8 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2&
//font->draw( ci, text_pos, p_item->cells[i].text, col,item_rect.size.x-check_w );
} break;
- case TreeItem::CELL_MODE_RANGE: {
+ case TreeItem::CELL_MODE_RANGE:
+ case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if (p_item->cells[i].text!="") {
@@ -1278,6 +1311,25 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2&
}
+int Tree::_count_selected_items(TreeItem* p_from) const {
+
+ int count=0;
+ for(int i=0;i<columns.size();i++) {
+ if (p_from->is_selected(i))
+ count++;
+ }
+
+ if (p_from->get_children()) {
+ count+=_count_selected_items(p_from->get_children());
+ }
+
+ if (p_from->get_next()) {
+ count+=_count_selected_items(p_from->get_next());
+ }
+
+ return count;
+
+}
void Tree::select_single_item(TreeItem *p_selected,TreeItem *p_current,int p_col,TreeItem *p_prev,bool *r_in_range) {
TreeItem::Cell &selected_cell=p_selected->cells[p_col];
@@ -1511,10 +1563,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
col_width-=w+cache.button_margin;
}
- if (p_button==BUTTON_LEFT) {
+ if (p_button==BUTTON_LEFT || (p_button==BUTTON_RIGHT && allow_rmb_select)) {
/* process selection */
- if (p_doubleclick && (!c.editable || c.mode==TreeItem::CELL_MODE_CUSTOM || c.mode==TreeItem::CELL_MODE_ICON || c.mode==TreeItem::CELL_MODE_CHECK)) {
+ if (p_doubleclick && (!c.editable || c.mode==TreeItem::CELL_MODE_CUSTOM || c.mode==TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it' s confusing for check
emit_signal("item_activated");
return -1;
@@ -1522,10 +1574,13 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
if (select_mode==SELECT_MULTI && p_mod.command && c.selectable) {
- if (!c.selected) {
+ if (!c.selected || p_button==BUTTON_RIGHT) {
p_item->select(col);
emit_signal("multi_selected",p_item,col,true);
+ if (p_button==BUTTON_RIGHT) {
+ emit_signal("item_rmb_selected",get_local_mouse_pos());
+ }
//p_item->selected_signal.call(col);
@@ -1545,8 +1600,26 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
bool inrange=false;
select_single_item( p_item, root, col,selected_item,&inrange );
+ if (p_button==BUTTON_RIGHT) {
+ emit_signal("item_rmb_selected",get_local_mouse_pos());
+ }
} else {
- select_single_item( p_item, root, col );
+
+ int icount = _count_selected_items(root);
+
+ if (select_mode==SELECT_MULTI && icount>1 && p_button!=BUTTON_RIGHT) {
+ single_select_defer=p_item;
+ single_select_defer_column=col;
+ } else {
+
+ if (p_button!=BUTTON_RIGHT || !c.selected) {
+ select_single_item( p_item, root, col );
+ }
+
+ if (p_button==BUTTON_RIGHT) {
+ emit_signal("item_rmb_selected",get_local_mouse_pos());
+ }
+ }
}
//if (!c.selected && select_mode==SELECT_MULTI) {
@@ -1566,7 +1639,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
/* editing */
- bool bring_up_editor=c.selected;// && already_selected;
+ bool bring_up_editor=force_select_on_already_selected ? (c.selected && already_selected) : c.selected;
bool bring_up_value_editor=false;
String editor_text=c.text;
@@ -1594,7 +1667,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_
}
} break;
- case TreeItem::CELL_MODE_RANGE: {
+ case TreeItem::CELL_MODE_RANGE:
+ case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
if (c.text!="") {
@@ -1794,6 +1868,13 @@ void Tree::text_editor_enter(String p_text) {
//popup_edited_item->edited_signal.call( popup_edited_item_col );
} break;
+ case TreeItem::CELL_MODE_RANGE_EXPRESSION: {
+
+ if(evaluator)
+ c.val=evaluator->eval(p_text);
+ else
+ c.val=p_text.to_double();
+ } break;
default: { ERR_FAIL(); }
}
@@ -2149,6 +2230,31 @@ void Tree::_input_event(InputEvent p_event) {
}
+ if (drop_mode_flags && root) {
+
+ Point2 mpos=Point2(b.x,b.y);
+ mpos -= cache.bg->get_offset();
+ mpos.y-=_get_title_button_height();
+ if (mpos.y>=0) {
+
+ if (h_scroll->is_visible())
+ mpos.x+=h_scroll->get_val();
+ if (v_scroll->is_visible())
+ mpos.y+=v_scroll->get_val();
+
+ int col,h,section;
+ TreeItem *it = _find_item_at_pos(root,mpos,col,h,section);
+
+ if (it!=drop_mode_over || section!=drop_mode_section) {
+ drop_mode_over=it;
+ drop_mode_section=section;
+ update();
+ }
+ }
+ }
+
+
+
if (cache.hover_type!=old_hover || cache.hover_index!=old_index) {
update();
}
@@ -2201,6 +2307,12 @@ void Tree::_input_event(InputEvent p_event) {
if (b.button_index==BUTTON_LEFT) {
+
+ if (single_select_defer) {
+ select_single_item( single_select_defer, root, single_select_defer_column );
+ single_select_defer=NULL;
+ }
+
range_click_timer->stop();
if (pressing_for_editor) {
@@ -2249,12 +2361,13 @@ void Tree::_input_event(InputEvent p_event) {
}
switch(b.button_index) {
+ case BUTTON_RIGHT:
case BUTTON_LEFT: {
Ref<StyleBox> bg = cache.bg;
Point2 pos = Point2(b.x,b.y) - bg->get_offset();
cache.click_type=Cache::CLICK_NONE;
- if (show_column_titles) {
+ if (show_column_titles && b.button_index==BUTTON_LEFT) {
pos.y-=_get_title_button_height();
if (pos.y<0) {
@@ -2276,8 +2389,12 @@ void Tree::_input_event(InputEvent p_event) {
}
}
- if (!root)
+ if (!root || (!root->get_children() && hide_root)) {
+ if (b.button_index==BUTTON_RIGHT && allow_rmb_select) {
+ emit_signal("empty_tree_rmb_selected",get_local_mouse_pos());
+ }
break;
+ }
click_handled=false;
pressing_for_editor=false;
@@ -2291,6 +2408,9 @@ void Tree::_input_event(InputEvent p_event) {
}
+ if (b.button_index==BUTTON_RIGHT)
+ break;
+
if (drag_touching) {
set_fixed_process(false);
drag_touching_deaccel=false;
@@ -2372,7 +2492,7 @@ bool Tree::edit_selected() {
item_edited(col,s);
return true;
- } else if (c.mode==TreeItem::CELL_MODE_RANGE && c.text!="") {
+ } else if ((c.mode==TreeItem::CELL_MODE_RANGE||c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION) && c.text!="") {
popup_menu->clear();
for (int i=0;i<c.text.get_slice_count(",");i++) {
@@ -2389,7 +2509,7 @@ bool Tree::edit_selected() {
popup_edited_item_col=col;
return true;
- } else if (c.mode==TreeItem::CELL_MODE_STRING || c.mode==TreeItem::CELL_MODE_RANGE) {
+ } else if (c.mode==TreeItem::CELL_MODE_STRING || c.mode==TreeItem::CELL_MODE_RANGE || c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION ) {
Point2i textedpos=get_global_pos() + rect.pos;
text_editor->set_pos( textedpos );
@@ -2398,7 +2518,7 @@ bool Tree::edit_selected() {
text_editor->set_text( c.mode==TreeItem::CELL_MODE_STRING?c.text:rtos(c.val) );
text_editor->select_all();
- if (c.mode==TreeItem::CELL_MODE_RANGE) {
+ if (c.mode==TreeItem::CELL_MODE_RANGE || c.mode==TreeItem::CELL_MODE_RANGE_EXPRESSION ) {
value_editor->set_pos(textedpos + Point2i(0,text_editor->get_size().height) );
value_editor->set_size( Size2(rect.size.width,1));
@@ -2512,6 +2632,15 @@ void Tree::_notification(int p_what) {
update_cache();;
}
+ if (p_what==NOTIFICATION_DRAG_END) {
+
+ drop_mode_flags=0;
+ update();
+ }
+ if (p_what==NOTIFICATION_DRAG_BEGIN) {
+
+ single_select_defer=NULL;
+ }
if (p_what==NOTIFICATION_FIXED_PROCESS) {
if (drag_touching) {
@@ -3125,7 +3254,7 @@ void Tree::_do_incr_search(const String& p_add) {
}
-TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_column,int &h) const {
+TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_column,int &h,int &section) const {
Point2 pos = p_pos;
@@ -3135,15 +3264,33 @@ TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_co
h = compute_item_height(p_item)+cache.vseparation;;
if (pos.y<h) {
+ if (drop_mode_flags==DROP_MODE_ON_ITEM) {
+ section=0;
+ } else if (drop_mode_flags==DROP_MODE_INBETWEEN) {
+ section=pos.y<h/2?-1:1;
+ } else if (pos.y<h/4) {
+ section=-1;
+ } else if (pos.y>=(h*3/4)) {
+ section=1;
+ } else {
+ section=0;
+ }
+
for(int i=0;i<columns.size();i++) {
int w = get_column_width(i);
if (pos.x < w) {
r_column=i;
+
+
return p_item;
}
pos.x-=w;
}
+
+
+
+
return NULL;
} else {
@@ -3162,7 +3309,7 @@ TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_co
int ch;
- TreeItem *r = _find_item_at_pos(n,pos,r_column,ch);
+ TreeItem *r = _find_item_at_pos(n,pos,r_column,ch,section);
pos.y-=ch;
h+=ch;
if (r)
@@ -3174,6 +3321,88 @@ TreeItem* Tree::_find_item_at_pos(TreeItem*p_item, const Point2& p_pos,int& r_co
}
+int Tree::get_column_at_pos(const Point2& p_pos) const {
+
+ if (root) {
+
+ Point2 pos=p_pos;
+ pos -= cache.bg->get_offset();
+ pos.y-=_get_title_button_height();
+ if (pos.y<0)
+ return -1;
+
+ if (h_scroll->is_visible())
+ pos.x+=h_scroll->get_val();
+ if (v_scroll->is_visible())
+ pos.y+=v_scroll->get_val();
+
+ int col,h,section;
+ TreeItem *it = _find_item_at_pos(root,pos,col,h,section);
+
+ if (it) {
+ return col;
+ }
+ }
+
+ return -1;
+
+}
+
+int Tree::get_drop_section_at_pos(const Point2& p_pos) const {
+
+ if (root) {
+
+ Point2 pos=p_pos;
+ pos -= cache.bg->get_offset();
+ pos.y-=_get_title_button_height();
+ if (pos.y<0)
+ return -100;
+
+ if (h_scroll->is_visible())
+ pos.x+=h_scroll->get_val();
+ if (v_scroll->is_visible())
+ pos.y+=v_scroll->get_val();
+
+ int col,h,section;
+ TreeItem *it = _find_item_at_pos(root,pos,col,h,section);
+
+ if (it) {
+ return section;
+ }
+ }
+
+ return -100;
+
+}
+TreeItem* Tree::get_item_at_pos(const Point2& p_pos) const {
+
+
+ if (root) {
+
+ Point2 pos=p_pos;
+ pos -= cache.bg->get_offset();
+ pos.y-=_get_title_button_height();
+ if (pos.y<0)
+ return NULL;
+
+ if (h_scroll->is_visible())
+ pos.x+=h_scroll->get_val();
+ if (v_scroll->is_visible())
+ pos.y+=v_scroll->get_val();
+
+ int col,h,section;
+ TreeItem *it = _find_item_at_pos(root,pos,col,h,section);
+
+ if (it) {
+
+ return it;
+ }
+ }
+
+ return NULL;
+
+}
+
String Tree::get_tooltip(const Point2& p_pos) const {
if (root) {
@@ -3189,8 +3418,8 @@ String Tree::get_tooltip(const Point2& p_pos) const {
if (v_scroll->is_visible())
pos.y+=v_scroll->get_val();
- int col,h;
- TreeItem *it = _find_item_at_pos(root,pos,col,h);
+ int col,h,section;
+ TreeItem *it = _find_item_at_pos(root,pos,col,h,section);
if (it) {
@@ -3227,6 +3456,46 @@ bool Tree::is_folding_hidden() const {
return hide_folding;
}
+void Tree::set_value_evaluator(ValueEvaluator *p_evaluator) {
+ evaluator = p_evaluator;
+}
+
+void Tree::set_drop_mode_flags(int p_flags) {
+ if (drop_mode_flags==p_flags)
+ return;
+ drop_mode_flags=p_flags;
+ if (drop_mode_flags==0) {
+ drop_mode_over=NULL;
+ }
+
+ update();
+}
+
+int Tree::get_drop_mode_flags() const {
+
+ return drop_mode_flags;
+}
+
+void Tree::set_single_select_cell_editing_only_when_already_selected(bool p_enable) {
+
+ force_select_on_already_selected=p_enable;
+}
+
+bool Tree::get_single_select_cell_editing_only_when_already_selected() const {
+
+ return force_select_on_already_selected;
+}
+
+
+void Tree::set_allow_rmb_select(bool p_allow) {
+
+ allow_rmb_select=p_allow;
+}
+
+bool Tree::get_allow_rmb_select() const{
+
+ return allow_rmb_select;
+}
void Tree::_bind_methods() {
@@ -3239,7 +3508,7 @@ void Tree::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_scroll_moved"),&Tree::_scroll_moved);
ObjectTypeDB::bind_method(_MD("clear"),&Tree::clear);
- ObjectTypeDB::bind_method(_MD("create_item:TreeItem","parent:TreeItem"),&Tree::_create_item,DEFVAL((Object*)NULL));
+ ObjectTypeDB::bind_method(_MD("create_item:TreeItem","parent:TreeItem"),&Tree::_create_item,DEFVAL(Variant()));
ObjectTypeDB::bind_method(_MD("get_root:TreeItem"),&Tree::get_root);
ObjectTypeDB::bind_method(_MD("set_column_min_width","column","min_width"),&Tree::set_column_min_width);
@@ -3260,6 +3529,8 @@ void Tree::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_edited_column"),&Tree::get_edited_column);
ObjectTypeDB::bind_method(_MD("get_custom_popup_rect"),&Tree::get_custom_popup_rect);
ObjectTypeDB::bind_method(_MD("get_item_area_rect","item:TreeItem","column"),&Tree::_get_item_rect,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("get_item_at_pos:TreeItem","pos"),&Tree::get_item_at_pos);
+ ObjectTypeDB::bind_method(_MD("get_column_at_pos","pos"),&Tree::get_column_at_pos);
ObjectTypeDB::bind_method(_MD("ensure_cursor_is_visible"),&Tree::ensure_cursor_is_visible);
@@ -3273,10 +3544,21 @@ void Tree::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_hide_folding","hide"),&Tree::set_hide_folding);
ObjectTypeDB::bind_method(_MD("is_folding_hidden"),&Tree::is_folding_hidden);
+ ObjectTypeDB::bind_method(_MD("set_drop_mode_flags","flags"),&Tree::set_drop_mode_flags);
+ ObjectTypeDB::bind_method(_MD("get_drop_mode_flags"),&Tree::get_drop_mode_flags);
+
+ ObjectTypeDB::bind_method(_MD("set_allow_rmb_select","allow"),&Tree::set_allow_rmb_select);
+ ObjectTypeDB::bind_method(_MD("get_allow_rmb_select"),&Tree::get_allow_rmb_select);
+
+
+ ObjectTypeDB::bind_method(_MD("set_single_select_cell_editing_only_when_already_selected","enable"),&Tree::set_single_select_cell_editing_only_when_already_selected);
+ ObjectTypeDB::bind_method(_MD("get_single_select_cell_editing_only_when_already_selected"),&Tree::get_single_select_cell_editing_only_when_already_selected);
ADD_SIGNAL( MethodInfo("item_selected"));
ADD_SIGNAL( MethodInfo("cell_selected"));
ADD_SIGNAL( MethodInfo("multi_selected",PropertyInfo(Variant::OBJECT,"item"),PropertyInfo(Variant::INT,"column"),PropertyInfo(Variant::BOOL,"selected")) );
+ ADD_SIGNAL( MethodInfo("item_rmb_selected",PropertyInfo(Variant::VECTOR2,"pos")));
+ ADD_SIGNAL( MethodInfo("empty_tree_rmb_selected",PropertyInfo(Variant::VECTOR2,"pos")));
ADD_SIGNAL( MethodInfo("item_edited"));
ADD_SIGNAL( MethodInfo("item_collapsed",PropertyInfo(Variant::OBJECT,"item")));
//ADD_SIGNAL( MethodInfo("item_doubleclicked" ) );
@@ -3287,6 +3569,11 @@ void Tree::_bind_methods() {
BIND_CONSTANT( SELECT_SINGLE );
BIND_CONSTANT( SELECT_ROW );
BIND_CONSTANT( SELECT_MULTI );
+
+ BIND_CONSTANT( DROP_MODE_DISABLED );
+ BIND_CONSTANT( DROP_MODE_ON_ITEM );
+ BIND_CONSTANT( DROP_MODE_INBETWEEN );
+
}
Tree::Tree() {
@@ -3367,6 +3654,15 @@ Tree::Tree() {
hide_folding=false;
+ evaluator=NULL;
+
+ drop_mode_flags=0;
+ drop_mode_over=NULL;
+ drop_mode_section=0;
+ single_select_defer=NULL;
+ force_select_on_already_selected=false;
+
+ allow_rmb_select=false;
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 1ba1c6a494..1dad26dffe 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -34,6 +34,7 @@
#include "scene/gui/line_edit.h"
#include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h"
+#include "core/helper/value_evaluator.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
@@ -52,6 +53,7 @@ public:
CELL_MODE_STRING, ///< just a string
CELL_MODE_CHECK, ///< string + check
CELL_MODE_RANGE, ///< Contains a range
+ CELL_MODE_RANGE_EXPRESSION, ///< Contains a range
CELL_MODE_ICON, ///< Contains a icon, not editable
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
};
@@ -77,7 +79,9 @@ friend class Tree;
bool custom_color;
Color color;
bool custom_bg_color;
+ bool custom_bg_outline;
Color bg_color;
+
Variant meta;
String tooltip;
@@ -224,7 +228,7 @@ public:
Color get_custom_color(int p_column) const;
void clear_custom_color(int p_column);
- void set_custom_bg_color(int p_column,const Color& p_color);
+ void set_custom_bg_color(int p_column, const Color& p_color, bool p_bg_outline=false);
void clear_custom_bg_color(int p_column);
Color get_custom_bg_color(int p_column) const;
@@ -255,6 +259,12 @@ public:
SELECT_MULTI
};
+ enum DropModeFlags {
+ DROP_MODE_DISABLED=0,
+ DROP_MODE_ON_ITEM=1,
+ DROP_MODE_INBETWEEN=2
+ };
+
private:
friend class TreeItem;
@@ -263,6 +273,11 @@ friend class TreeItem;
TreeItem *selected_item;
TreeItem *edited_item;
+ TreeItem *drop_mode_over;
+ int drop_mode_section;
+
+ TreeItem *single_select_defer;
+ int single_select_defer_column;
int pressed_button;
bool pressing_for_editor;
@@ -287,6 +302,8 @@ friend class TreeItem;
int blocked;
+ int drop_mode_flags;
+
struct ColumnInfo {
int min_width;
@@ -359,6 +376,8 @@ friend class TreeItem;
Color font_color;
Color font_color_selected;
Color guide_color;
+ Color drop_position_color;
+
int hseparation;
int vseparation;
int item_margin;
@@ -403,7 +422,7 @@ friend class TreeItem;
TreeItem* _search_item_text(TreeItem *p_at, const String& p_find,int *r_col,bool p_selectable,bool p_backwards=false);
- TreeItem* _find_item_at_pos(TreeItem *p_current, const Point2& p_pos,int& r_column,int &h) const;
+ TreeItem* _find_item_at_pos(TreeItem *p_current, const Point2& p_pos, int& r_column, int &h, int &section) const;
/* float drag_speed;
float drag_accum;
@@ -419,9 +438,16 @@ friend class TreeItem;
bool drag_touching;
bool drag_touching_deaccel;
bool click_handled;
+ bool allow_rmb_select;
+
+ bool force_select_on_already_selected;
bool hide_folding;
+ ValueEvaluator *evaluator;
+
+ int _count_selected_items(TreeItem* p_from) const;
+
protected:
static void _bind_methods();
@@ -429,10 +455,16 @@ protected:
Object* _create_item(Object *p_parent) { return create_item(p_parent->cast_to<TreeItem>() ); }
TreeItem *_get_next_selected(Object *p_item) { return get_next_selected(p_item->cast_to<TreeItem>() ); }
Rect2 _get_item_rect(Object *p_item,int p_column) const { return get_item_rect(p_item->cast_to<TreeItem>(),p_column ); }
+
+
public:
virtual String get_tooltip(const Point2& p_pos) const;
+ TreeItem* get_item_at_pos(const Point2& p_pos) const;
+ int get_column_at_pos(const Point2& p_pos) const;
+ int get_drop_section_at_pos(const Point2& p_pos) const;
+
void clear();
TreeItem* create_item(TreeItem *p_parent=0);
@@ -482,7 +514,16 @@ public:
void set_hide_folding(bool p_hide);
bool is_folding_hidden() const;
+ void set_drop_mode_flags(int p_flags);
+ int get_drop_mode_flags() const;
+
+ void set_single_select_cell_editing_only_when_already_selected(bool p_enable);
+ bool get_single_select_cell_editing_only_when_already_selected() const;
+
+ void set_allow_rmb_select(bool p_allow);
+ bool get_allow_rmb_select() const;
+ void set_value_evaluator(ValueEvaluator *p_evaluator);
Tree();
~Tree();
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index fc7cc0a362..26227d6389 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -131,7 +131,7 @@ void VideoPlayer::_notification(int p_notification) {
if (!playback->is_playing())
return;
- double audio_time = OS::get_singleton()->get_ticks_usec()/1000000.0; //AudioServer::get_singleton()->get_mix_time();
+ double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()); //AudioServer::get_singleton()->get_mix_time();
double delta = last_audio_time==0?0:audio_time-last_audio_time;
last_audio_time=audio_time;
@@ -360,8 +360,8 @@ bool VideoPlayer::has_autoplay() const {
void VideoPlayer::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&VideoPlayer::set_stream);
- ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&VideoPlayer::get_stream);
+ ObjectTypeDB::bind_method(_MD("set_stream","stream:VideoStream"),&VideoPlayer::set_stream);
+ ObjectTypeDB::bind_method(_MD("get_stream:VideoStream"),&VideoPlayer::get_stream);
ObjectTypeDB::bind_method(_MD("play"),&VideoPlayer::play);
ObjectTypeDB::bind_method(_MD("stop"),&VideoPlayer::stop);
diff --git a/scene/io/resource_format_image.cpp b/scene/io/resource_format_image.cpp
index 6174d134a3..f3a0eaa8c4 100644
--- a/scene/io/resource_format_image.cpp
+++ b/scene/io/resource_format_image.cpp
@@ -108,7 +108,7 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String& p_origin
Error err = ImageLoader::load_image(p_path,&image);
if (!err && debug_load_times) {
- double total=(double)(OS::get_singleton()->get_ticks_usec()-begtime)/1000000.0;
+ double total=USEC_TO_SEC((OS::get_singleton()->get_ticks_usec()-begtime));
print_line("IMAGE: "+itos(image.get_width())+"x"+itos(image.get_height()));
print_line(" -load: "+rtos(total));
}
@@ -201,7 +201,7 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String& p_origin
if (debug_load_times) {
- total=(double)(OS::get_singleton()->get_ticks_usec()-begtime)/1000000.0;
+ total=USEC_TO_SEC(OS::get_singleton()->get_ticks_usec()-begtime);
print_line(" -make texture: "+rtos(total));
}
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index e921795628..c31a57f819 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -94,13 +94,13 @@ Vector2 CanvasLayer::get_offset() const {
}
-void CanvasLayer::set_rotation(real_t p_rotation) {
+void CanvasLayer::set_rotation(real_t p_radians) {
if (locrotscale_dirty)
_update_locrotscale();
- rot=p_rotation;
+ rot=p_radians;
_update_xform();
}
@@ -113,6 +113,29 @@ real_t CanvasLayer::get_rotation() const {
return rot;
}
+void CanvasLayer::set_rotationd(real_t p_degrees) {
+
+ set_rotation(Math::deg2rad(p_degrees));
+}
+
+real_t CanvasLayer::get_rotationd() const {
+
+ return Math::rad2deg(get_rotation());
+}
+
+// Kept for compatibility after rename to {s,g}et_rotationd.
+// Could be removed after a couple releases.
+void CanvasLayer::_set_rotationd(real_t p_degrees) {
+
+ WARN_PRINT("Deprecated method CanvasLayer._set_rotationd(): This method was renamed to set_rotationd. Please adapt your code accordingly, as the old method will be obsoleted.");
+ set_rotationd(p_degrees);
+}
+
+real_t CanvasLayer::_get_rotationd() const {
+
+ WARN_PRINT("Deprecated method CanvasLayer._get_rotationd(): This method was renamed to get_rotationd. Please adapt your code accordingly, as the old method will be obsoleted.");
+ return get_rotationd();
+}
void CanvasLayer::set_scale(const Vector2& p_scale) {
@@ -122,7 +145,6 @@ void CanvasLayer::set_scale(const Vector2& p_scale) {
scale=p_scale;
_update_xform();
-
}
Vector2 CanvasLayer::get_scale() const {
@@ -193,16 +215,6 @@ RID CanvasLayer::get_viewport() const {
return viewport;
}
-void CanvasLayer::_set_rotationd(real_t p_rotation) {
-
- set_rotation(Math::deg2rad(p_rotation));
-}
-
-real_t CanvasLayer::_get_rotationd() const {
-
- return Math::rad2deg(get_rotation());
-}
-
void CanvasLayer::_bind_methods() {
@@ -216,22 +228,26 @@ void CanvasLayer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_offset","offset"),&CanvasLayer::set_offset);
ObjectTypeDB::bind_method(_MD("get_offset"),&CanvasLayer::get_offset);
- ObjectTypeDB::bind_method(_MD("set_rotation","rotation"),&CanvasLayer::set_rotation);
+ ObjectTypeDB::bind_method(_MD("set_rotation","radians"),&CanvasLayer::set_rotation);
ObjectTypeDB::bind_method(_MD("get_rotation"),&CanvasLayer::get_rotation);
- ObjectTypeDB::bind_method(_MD("_set_rotationd","rotationd"),&CanvasLayer::_set_rotationd);
+ ObjectTypeDB::bind_method(_MD("set_rotationd","degrees"),&CanvasLayer::set_rotationd);
+ ObjectTypeDB::bind_method(_MD("get_rotationd"),&CanvasLayer::get_rotationd);
+
+ // TODO: Obsolete those two methods (old name) properly (GH-4397)
+ ObjectTypeDB::bind_method(_MD("_set_rotationd","degrees"),&CanvasLayer::_set_rotationd);
ObjectTypeDB::bind_method(_MD("_get_rotationd"),&CanvasLayer::_get_rotationd);
ObjectTypeDB::bind_method(_MD("set_scale","scale"),&CanvasLayer::set_scale);
ObjectTypeDB::bind_method(_MD("get_scale"),&CanvasLayer::get_scale);
- ObjectTypeDB::bind_method(_MD("get_world_2d:Canvas"),&CanvasLayer::get_world_2d);
+ ObjectTypeDB::bind_method(_MD("get_world_2d:World2D"),&CanvasLayer::get_world_2d);
ObjectTypeDB::bind_method(_MD("get_viewport"),&CanvasLayer::get_viewport);
ADD_PROPERTY( PropertyInfo(Variant::INT,"layer",PROPERTY_HINT_RANGE,"-128,128,1"),_SCS("set_layer"),_SCS("get_layer") );
//ADD_PROPERTY( PropertyInfo(Variant::MATRIX32,"transform",PROPERTY_HINT_RANGE),_SCS("set_transform"),_SCS("get_transform") );
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_offset"),_SCS("get_offset") );
- ADD_PROPERTY( PropertyInfo(Variant::REAL,"rotation"),_SCS("_set_rotationd"),_SCS("_get_rotationd") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"rotation"),_SCS("set_rotationd"),_SCS("get_rotationd") );
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scale"),_SCS("set_scale"),_SCS("get_scale") );
}
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index 809b3fae7f..a3e8211a43 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -48,7 +48,7 @@ class CanvasLayer : public Node {
RID viewport;
Viewport *vp;
-
+ // Deprecated, should be removed in a future version.
void _set_rotationd(real_t p_rotation);
real_t _get_rotationd() const;
@@ -70,9 +70,12 @@ public:
void set_offset(const Vector2& p_offset);
Vector2 get_offset() const;
- void set_rotation(real_t p_rotation);
+ void set_rotation(real_t p_radians);
real_t get_rotation() const;
+ void set_rotationd(real_t p_degrees);
+ real_t get_rotationd() const;
+
void set_scale(const Vector2& p_scale);
Vector2 get_scale() const;
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 90e3713799..2320616629 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -480,8 +480,9 @@ void HTTPRequest::_bind_methods() {
BIND_CONSTANT( RESULT_NO_RESPONSE );
BIND_CONSTANT( RESULT_BODY_SIZE_LIMIT_EXCEEDED );
BIND_CONSTANT( RESULT_REQUEST_FAILED );
- BIND_CONSTANT( RESULT_REDIRECT_LIMIT_REACHED );
+ BIND_CONSTANT( RESULT_DOWNLOAD_FILE_CANT_OPEN );
BIND_CONSTANT( RESULT_DOWNLOAD_FILE_WRITE_ERROR );
+ BIND_CONSTANT( RESULT_REDIRECT_LIMIT_REACHED );
}
@@ -494,7 +495,7 @@ HTTPRequest::HTTPRequest()
max_redirects=8;
body_len=-1;
got_response=false;
- validate_ssl=false;
+ validate_ssl=false;
use_ssl=false;
response_code=0;
request_sent=false;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index b90edb38b2..29925b62f5 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -749,6 +749,16 @@ void Node::add_child(Node *p_child, bool p_legible_unique_name) {
}
+void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_legible_unique_name) {
+ add_child(p_child, p_legible_unique_name);
+
+ if (is_a_parent_of(p_node)) {
+ move_child(p_child, p_node->get_position_in_parent() + 1);
+ } else {
+ WARN_PRINTS("Cannot move under node " + p_node->get_name() + " as " + p_child->get_name() + " does not share a parent")
+ }
+}
+
void Node::_propagate_validate_owner() {
@@ -1234,7 +1244,19 @@ void Node::get_groups(List<GroupInfo> *p_groups) const {
}
+bool Node::has_persistent_groups() const {
+ const StringName *K=NULL;
+
+ while ((K=data.grouped.next(K))) {
+
+ if (data.grouped[*K].persistent)
+ return true;
+ }
+
+ return false;
+
+}
void Node::_print_tree(const Node *p_node) {
print_line(String(p_node->get_path_to(this)));
@@ -1374,6 +1396,16 @@ bool Node::is_editable_instance(Node *p_node) const {
return data.editable_instances.has(p);
}
+void Node::set_editable_instances(const HashMap<NodePath,int>& p_editable_instances) {
+
+ data.editable_instances=p_editable_instances;
+}
+
+HashMap<NodePath,int> Node::get_editable_instances() const {
+
+ return data.editable_instances;
+}
+
#if 0
@@ -2000,8 +2032,27 @@ void Node::clear_internal_tree_resource_paths() {
}
+String Node::get_configuration_warning() const {
+
+ return String();
+}
+
+void Node::update_configuration_warning() {
+
+#ifdef TOOLS_ENABLED
+ if (!is_inside_tree())
+ return;
+ if (get_tree()->get_edited_scene_root() && (get_tree()->get_edited_scene_root()==this || get_tree()->get_edited_scene_root()->is_a_parent_of(this))) {
+ get_tree()->emit_signal(SceneStringNames::get_singleton()->node_configuration_warning_changed,this);
+ }
+#endif
+
+}
+
void Node::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("_add_child_below_node","node:Node","child_node:Node","legible_unique_name"),&Node::add_child_below_node,DEFVAL(false));
+
ObjectTypeDB::bind_method(_MD("set_name","name"),&Node::set_name);
ObjectTypeDB::bind_method(_MD("get_name"),&Node::get_name);
ObjectTypeDB::bind_method(_MD("add_child","node:Node","legible_unique_name"),&Node::add_child,DEFVAL(false));
@@ -2012,7 +2063,7 @@ void Node::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_child:Node","idx"),&Node::get_child);
ObjectTypeDB::bind_method(_MD("has_node","path"),&Node::has_node);
ObjectTypeDB::bind_method(_MD("get_node:Node","path"),&Node::get_node);
- ObjectTypeDB::bind_method(_MD("get_parent:Parent"),&Node::get_parent);
+ ObjectTypeDB::bind_method(_MD("get_parent:Node"),&Node::get_parent);
ObjectTypeDB::bind_method(_MD("find_node:Node","mask","recursive","owned"),&Node::find_node,DEFVAL(true),DEFVAL(true));
ObjectTypeDB::bind_method(_MD("has_node_and_resource","path"),&Node::has_node_and_resource);
ObjectTypeDB::bind_method(_MD("get_node_and_resource","path"),&Node::_get_node_and_resource);
@@ -2066,6 +2117,10 @@ void Node::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_viewport"),&Node::get_viewport);
ObjectTypeDB::bind_method(_MD("queue_free"),&Node::queue_delete);
+
+
+
+
#ifdef TOOLS_ENABLED
ObjectTypeDB::bind_method(_MD("_set_import_path","import_path"),&Node::set_import_path);
ObjectTypeDB::bind_method(_MD("_get_import_path"),&Node::get_import_path);
@@ -2085,6 +2140,8 @@ void Node::_bind_methods() {
BIND_CONSTANT( NOTIFICATION_PAUSED );
BIND_CONSTANT( NOTIFICATION_UNPAUSED );
BIND_CONSTANT( NOTIFICATION_INSTANCED );
+ BIND_CONSTANT( NOTIFICATION_DRAG_BEGIN );
+ BIND_CONSTANT( NOTIFICATION_DRAG_END );
diff --git a/scene/main/node.h b/scene/main/node.h
index 5c6147d644..cf62e7cdea 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -180,6 +180,8 @@ public:
NOTIFICATION_PARENTED=18,
NOTIFICATION_UNPARENTED=19,
NOTIFICATION_INSTANCED=20,
+ NOTIFICATION_DRAG_BEGIN=21,
+ NOTIFICATION_DRAG_END=22,
};
/* NODE/TREE */
@@ -188,6 +190,7 @@ public:
void set_name(const String& p_name);
void add_child(Node *p_child,bool p_legible_unique_name=false);
+ void add_child_below_node(Node *p_node, Node *p_child, bool p_legible_unique_name=false);
void remove_child(Node *p_child);
int get_child_count() const;
@@ -220,6 +223,7 @@ public:
};
void get_groups(List<GroupInfo> *p_groups) const;
+ bool has_persistent_groups() const;
void move_child(Node *p_child,int p_pos);
void raise();
@@ -239,6 +243,8 @@ public:
void set_editable_instance(Node* p_node,bool p_editable);
bool is_editable_instance(Node* p_node) const;
+ void set_editable_instances(const HashMap<NodePath,int>& p_editable_instances);
+ HashMap<NodePath,int> get_editable_instances() const;
/* NOTIFICATIONS */
@@ -312,6 +318,10 @@ public:
_FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; }
+ virtual String get_configuration_warning() const;
+
+ void update_configuration_warning();
+
/* CANVAS */
Node();
diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp
index cc103b5115..d7f22c3228 100644
--- a/scene/main/scene_main_loop.cpp
+++ b/scene/main/scene_main_loop.cpp
@@ -1002,6 +1002,7 @@ static void _fill_array(Node *p_node, Array& array, int p_level) {
array.push_back(p_level);
array.push_back(p_node->get_name());
array.push_back(p_node->get_type());
+ array.push_back(p_node->get_instance_ID());
for(int i=0;i<p_node->get_child_count();i++) {
_fill_array(p_node->get_child(i),array,p_level+1);
@@ -1592,6 +1593,14 @@ void SceneTree::_live_edit_reparent_node_func(const NodePath& p_at,const NodePat
#endif
+
+
+void SceneTree::drop_files(const Vector<String>& p_files,int p_from_screen) {
+
+ emit_signal("files_dropped",p_files,p_from_screen);
+ MainLoop::drop_files(p_files,p_from_screen);
+}
+
void SceneTree::_bind_methods() {
@@ -1660,10 +1669,13 @@ void SceneTree::_bind_methods() {
ADD_SIGNAL( MethodInfo("tree_changed") );
ADD_SIGNAL( MethodInfo("node_removed",PropertyInfo( Variant::OBJECT, "node") ) );
ADD_SIGNAL( MethodInfo("screen_resized") );
+ ADD_SIGNAL( MethodInfo("node_configuration_warning_changed",PropertyInfo( Variant::OBJECT, "node")) );
ADD_SIGNAL( MethodInfo("idle_frame"));
ADD_SIGNAL( MethodInfo("fixed_frame"));
+ ADD_SIGNAL( MethodInfo("files_dropped",PropertyInfo(Variant::STRING_ARRAY,"files"),PropertyInfo(Variant::INT,"screen")) );
+
BIND_CONSTANT( GROUP_CALL_DEFAULT );
BIND_CONSTANT( GROUP_CALL_REVERSE );
BIND_CONSTANT( GROUP_CALL_REALTIME );
diff --git a/scene/main/scene_main_loop.h b/scene/main/scene_main_loop.h
index b6a015c9ed..5fc9e0c1ae 100644
--- a/scene/main/scene_main_loop.h
+++ b/scene/main/scene_main_loop.h
@@ -344,6 +344,7 @@ public:
static SceneTree* get_singleton() { return singleton; }
+ void drop_files(const Vector<String>& p_files,int p_from_screen=0);
SceneTree();
~SceneTree();
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index 73458e03c0..7d9bbd7f4f 100644
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -121,6 +121,20 @@ void Timer::stop() {
autostart=false;
}
+
+void Timer::set_active(bool p_active) {
+ if (active == p_active)
+ return;
+
+ active = p_active;
+ _set_process(processing);
+
+}
+
+bool Timer::is_active() const {
+ return active;
+}
+
float Timer::get_time_left() const {
return time_left >0 ? time_left : 0;
@@ -157,9 +171,10 @@ Timer::TimerProcessMode Timer::get_timer_process_mode() const{
void Timer::_set_process(bool p_process, bool p_force)
{
switch (timer_process_mode) {
- case TIMER_PROCESS_FIXED: set_fixed_process(p_process); break;
- case TIMER_PROCESS_IDLE: set_process(p_process); break;
+ case TIMER_PROCESS_FIXED: set_fixed_process(p_process && active); break;
+ case TIMER_PROCESS_IDLE: set_process(p_process && active); break;
}
+ processing = p_process;
}
void Timer::_bind_methods() {
@@ -176,6 +191,9 @@ void Timer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("start"),&Timer::start);
ObjectTypeDB::bind_method(_MD("stop"),&Timer::stop);
+ ObjectTypeDB::bind_method(_MD("set_active", "active"), &Timer::set_active);
+ ObjectTypeDB::bind_method(_MD("is_active"), &Timer::is_active);
+
ObjectTypeDB::bind_method(_MD("get_time_left"),&Timer::get_time_left);
ObjectTypeDB::bind_method(_MD("set_timer_process_mode", "mode"), &Timer::set_timer_process_mode);
@@ -198,5 +216,7 @@ Timer::Timer() {
autostart=false;
wait_time=1;
one_shot=false;
- time_left=-1;
+ time_left = -1;
+ processing = false;
+ active = true;
}
diff --git a/scene/main/timer.h b/scene/main/timer.h
index 0baea76fad..688be6e4f2 100644
--- a/scene/main/timer.h
+++ b/scene/main/timer.h
@@ -38,6 +38,8 @@ class Timer : public Node {
float wait_time;
bool one_shot;
bool autostart;
+ bool processing;
+ bool active;
double time_left;
protected:
@@ -62,6 +64,8 @@ public:
void start();
void stop();
+ void set_active(bool p_active);
+ bool is_active() const;
float get_time_left() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 5017ae61ff..49ecfee548 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -37,6 +37,7 @@
#include "servers/spatial_sound_2d_server.h"
#include "scene/gui/control.h"
#include "scene/3d/camera.h"
+#include "scene/3d/listener.h"
#include "scene/resources/mesh.h"
#include "scene/3d/spatial_indexer.h"
#include "scene/3d/collision_object.h"
@@ -388,6 +389,19 @@ void Viewport::_notification(int p_what) {
} break;
case NOTIFICATION_READY: {
#ifndef _3D_DISABLED
+ if (listeners.size() && !listener) {
+ Listener *first=NULL;
+ for(Set<Listener*>::Element *E=listeners.front();E;E=E->next()) {
+
+ if (first==NULL || first->is_greater_than(E->get())) {
+ first=E->get();
+ }
+ }
+
+ if (first)
+ first->make_current();
+ }
+
if (cameras.size() && !camera) {
//there are cameras but no current camera, pick first in tree and make it current
Camera *first=NULL;
@@ -414,7 +428,7 @@ void Viewport::_notification(int p_what) {
_vp_exit_tree();
VisualServer::get_singleton()->viewport_set_scenario(viewport,RID());
- SpatialSoundServer::get_singleton()->listener_set_space(listener,RID());
+ SpatialSoundServer::get_singleton()->listener_set_space(internal_listener, RID());
VisualServer::get_singleton()->viewport_remove_canvas(viewport,current_canvas);
if (contact_2d_debug.is_valid()) {
VisualServer::get_singleton()->free(contact_2d_debug);
@@ -740,10 +754,10 @@ Rect2 Viewport::get_rect() const {
void Viewport::_update_listener() {
- if (is_inside_tree() && audio_listener && camera && (!get_parent() || (get_parent()->cast_to<Control>() && get_parent()->cast_to<Control>()->is_visible()))) {
- SpatialSoundServer::get_singleton()->listener_set_space(listener,find_world()->get_sound_space());
+ if (is_inside_tree() && audio_listener && (camera || listener) && (!get_parent() || (get_parent()->cast_to<Control>() && get_parent()->cast_to<Control>()->is_visible()))) {
+ SpatialSoundServer::get_singleton()->listener_set_space(internal_listener, find_world()->get_sound_space());
} else {
- SpatialSoundServer::get_singleton()->listener_set_space(listener,RID());
+ SpatialSoundServer::get_singleton()->listener_set_space(internal_listener, RID());
}
@@ -752,9 +766,9 @@ void Viewport::_update_listener() {
void Viewport::_update_listener_2d() {
if (is_inside_tree() && audio_listener && (!get_parent() || (get_parent()->cast_to<Control>() && get_parent()->cast_to<Control>()->is_visible())))
- SpatialSound2DServer::get_singleton()->listener_set_space(listener_2d,find_world_2d()->get_sound_space());
+ SpatialSound2DServer::get_singleton()->listener_set_space(internal_listener_2d, find_world_2d()->get_sound_space());
else
- SpatialSound2DServer::get_singleton()->listener_set_space(listener_2d,RID());
+ SpatialSound2DServer::get_singleton()->listener_set_space(internal_listener_2d, RID());
}
@@ -798,11 +812,11 @@ void Viewport::set_canvas_transform(const Matrix32& p_transform) {
Matrix32 xform = (global_canvas_transform * canvas_transform).affine_inverse();
Size2 ss = get_visible_rect().size;
- SpatialSound2DServer::get_singleton()->listener_set_transform(listener_2d,Matrix32(0,xform.xform(ss*0.5)));
+ SpatialSound2DServer::get_singleton()->listener_set_transform(internal_listener_2d, Matrix32(0, xform.xform(ss*0.5)));
Vector2 ss2 = ss*xform.get_scale();
float panrange = MAX(ss2.x,ss2.y);
- SpatialSound2DServer::get_singleton()->listener_set_param(listener_2d,SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE,panrange);
+ SpatialSound2DServer::get_singleton()->listener_set_param(internal_listener_2d, SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE, panrange);
}
@@ -823,11 +837,11 @@ void Viewport::_update_global_transform() {
Matrix32 xform = (sxform * canvas_transform).affine_inverse();
Size2 ss = get_visible_rect().size;
- SpatialSound2DServer::get_singleton()->listener_set_transform(listener_2d,Matrix32(0,xform.xform(ss*0.5)));
+ SpatialSound2DServer::get_singleton()->listener_set_transform(internal_listener_2d, Matrix32(0, xform.xform(ss*0.5)));
Vector2 ss2 = ss*xform.get_scale();
float panrange = MAX(ss2.x,ss2.y);
- SpatialSound2DServer::get_singleton()->listener_set_param(listener_2d,SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE,panrange);
+ SpatialSound2DServer::get_singleton()->listener_set_param(internal_listener_2d, SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE, panrange);
}
@@ -846,12 +860,75 @@ Matrix32 Viewport::get_global_canvas_transform() const{
return global_canvas_transform;
}
+void Viewport::_listener_transform_changed_notify() {
+
+#ifndef _3D_DISABLED
+ if (listener)
+ SpatialSoundServer::get_singleton()->listener_set_transform(internal_listener, listener->get_listener_transform());
+#endif
+}
+
+void Viewport::_listener_set(Listener* p_listener) {
+
+#ifndef _3D_DISABLED
+
+ if (listener == p_listener)
+ return;
+
+ listener = p_listener;
+
+ _update_listener();
+ _listener_transform_changed_notify();
+#endif
+}
+
+bool Viewport::_listener_add(Listener* p_listener) {
+
+ listeners.insert(p_listener);
+ return listeners.size() == 1;
+}
+
+void Viewport::_listener_remove(Listener* p_listener) {
+
+ listeners.erase(p_listener);
+ if (listener == p_listener) {
+ listener = NULL;
+ }
+}
+
+#ifndef _3D_DISABLED
+void Viewport::_listener_make_next_current(Listener* p_exclude) {
+
+ if (listeners.size() > 0) {
+ for (Set<Listener*>::Element *E = listeners.front(); E; E = E->next()) {
+
+ if (p_exclude == E->get())
+ continue;
+ if (!E->get()->is_inside_tree())
+ continue;
+ if (listener != NULL)
+ return;
+
+ E->get()->make_current();
+
+ }
+ }
+ else {
+ // Attempt to reset listener to the camera position
+ if (camera != NULL) {
+ _update_listener();
+ _camera_transform_changed_notify();
+ }
+ }
+}
+#endif
void Viewport::_camera_transform_changed_notify() {
#ifndef _3D_DISABLED
- if (camera)
- SpatialSoundServer::get_singleton()->listener_set_transform(listener,camera->get_camera_transform());
+ // If there is an active listener in the scene, it takes priority over the camera
+ if (camera && !listener)
+ SpatialSoundServer::get_singleton()->listener_set_transform(internal_listener, camera->get_camera_transform());
#endif
}
@@ -985,6 +1062,16 @@ void Viewport::_propagate_enter_world(Node *p_node) {
}
}
+void Viewport::_propagate_viewport_notification(Node* p_node,int p_what) {
+
+ p_node->notification(p_what);
+ for(int i=0;i<p_node->get_child_count();i++) {
+ Node *c = p_node->get_child(i);
+ if (c->cast_to<Viewport>())
+ continue;
+ _propagate_viewport_notification(c,p_what);
+ }
+}
void Viewport::_propagate_exit_world(Node *p_node) {
@@ -1066,6 +1153,11 @@ Ref<World> Viewport::find_world() const{
return Ref<World>();
}
+Listener* Viewport::get_listener() const {
+
+ return listener;
+}
+
Camera* Viewport::get_camera() const {
return camera;
@@ -1147,6 +1239,8 @@ void Viewport::set_as_render_target(bool p_enable){
render_target_texture->set_flags(render_target_texture->flags);
render_target_texture->emit_changed();
+
+ update_configuration_warning();
}
bool Viewport::is_set_as_render_target() const{
@@ -1346,7 +1440,6 @@ void Viewport::_vp_unhandled_input(const InputEvent& p_ev) {
if (disable_input)
return;
-
#ifdef TOOLS_ENABLED
if (get_tree()->is_editor_hint() && get_tree()->get_edited_scene_root()->is_a_parent_of(this)) {
return;
@@ -1481,22 +1574,30 @@ void Viewport::_gui_call_input(Control *p_control,const InputEvent& p_input) {
// _block();
- while(p_control) {
-
- p_control->call_multilevel(SceneStringNames::get_singleton()->_input_event,p_input);
- if (gui.key_event_accepted)
- break;
- if (!p_control->is_inside_tree())
- break;
- p_control->emit_signal(SceneStringNames::get_singleton()->input_event,p_input);
- if (!p_control->is_inside_tree() || p_control->is_set_as_toplevel()) {
- break;
- }
- if (gui.key_event_accepted)
- break;
- if (p_control->data.stop_mouse && (p_input.type==InputEvent::MOUSE_BUTTON || p_input.type==InputEvent::MOUSE_MOTION))
- break;
- p_control=p_control->data.parent;
+ CanvasItem *ci=p_control;
+ while(ci) {
+
+ Control *control = ci->cast_to<Control>();
+ if (control) {
+ control->call_multilevel(SceneStringNames::get_singleton()->_input_event,p_input);
+ if (gui.key_event_accepted)
+ break;
+ if (!control->is_inside_tree())
+ break;
+ control->emit_signal(SceneStringNames::get_singleton()->input_event,p_input);
+ if (!control->is_inside_tree() || control->is_set_as_toplevel()) {
+ break;
+ }
+ if (gui.key_event_accepted)
+ break;
+ if (control->data.stop_mouse && (p_input.type==InputEvent::MOUSE_BUTTON || p_input.type==InputEvent::MOUSE_MOTION))
+ break;
+ }
+
+ if (ci->is_set_as_toplevel())
+ break;
+
+ ci=ci->get_parent_item();
}
//_unblock();
@@ -1676,6 +1777,9 @@ void Viewport::_gui_input_event(InputEvent p_event) {
if (p_event.mouse_button.button_index==BUTTON_LEFT) {
gui.drag_accum=Vector2();
gui.drag_attempted=false;
+ if (gui.drag_data.get_type()!=Variant::NIL) {
+ _propagate_viewport_notification(this,NOTIFICATION_DRAG_END);
+ }
gui.drag_data=Variant();
}
@@ -1736,6 +1840,7 @@ void Viewport::_gui_input_event(InputEvent p_event) {
gui.mouse_over->drop_data(pos,gui.drag_data);
gui.drag_data=Variant();
+ _propagate_viewport_notification(this,NOTIFICATION_DRAG_END);
//change mouse accordingly
}
@@ -1759,6 +1864,7 @@ void Viewport::_gui_input_event(InputEvent p_event) {
}
if (gui.drag_data.get_type()!=Variant::NIL && p_event.mouse_button.button_index==BUTTON_LEFT) {
+ _propagate_viewport_notification(this,NOTIFICATION_DRAG_END);
gui.drag_data=Variant(); //always clear
}
@@ -1790,6 +1896,10 @@ void Viewport::_gui_input_event(InputEvent p_event) {
gui.mouse_focus=NULL;
}
gui.drag_attempted=true;
+ if (gui.drag_data.get_type()!=Variant::NIL) {
+
+ _propagate_viewport_notification(this,NOTIFICATION_DRAG_BEGIN);
+ }
}
}
@@ -1814,11 +1924,15 @@ void Viewport::_gui_input_event(InputEvent p_event) {
}
}
+
+
if (over!=gui.mouse_over) {
if (gui.mouse_over)
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT);
+ _gui_cancel_tooltip();
+
if (over)
over->notification(Control::NOTIFICATION_MOUSE_ENTER);
@@ -1826,8 +1940,6 @@ void Viewport::_gui_input_event(InputEvent p_event) {
gui.mouse_over=over;
- _gui_cancel_tooltip();
-
if (gui.drag_preview) {
gui.drag_preview->set_pos(mpos);
}
@@ -1896,7 +2008,15 @@ void Viewport::_gui_input_event(InputEvent p_event) {
if (gui.drag_data.get_type()!=Variant::NIL && p_event.mouse_motion.button_mask&BUTTON_MASK_LEFT) {
- /*bool can_drop =*/ over->can_drop_data(pos,gui.drag_data);
+
+ bool can_drop = over->can_drop_data(pos,gui.drag_data);
+
+ if (!can_drop) {
+ OS::get_singleton()->set_cursor_shape( OS::CURSOR_FORBIDDEN );
+ } else {
+ OS::get_singleton()->set_cursor_shape( OS::CURSOR_CAN_DROP );
+
+ }
//change mouse accordingly i guess
}
@@ -2367,6 +2487,21 @@ bool Viewport::is_input_disabled() const {
return disable_input;
}
+Variant Viewport::gui_get_drag_data() const {
+ return gui.drag_data;
+}
+
+
+String Viewport::get_configuration_warning() const {
+
+ if (get_parent() && !get_parent()->cast_to<Control>() && !render_target) {
+
+ return TTR("This viewport is not set as render target. If you intend for it to display its contents directly to the screen, make it a child of a Control so it can obtain a size. Otherwise, make it a RenderTarget and assign its internal texture to some node for display.");
+ }
+
+ return String();
+}
+
void Viewport::_bind_methods() {
@@ -2452,6 +2587,7 @@ void Viewport::_bind_methods() {
ObjectTypeDB::bind_method(_MD("warp_mouse","to_pos"), &Viewport::warp_mouse);
ObjectTypeDB::bind_method(_MD("gui_has_modal_stack"), &Viewport::gui_has_modal_stack);
+ ObjectTypeDB::bind_method(_MD("gui_get_drag_data:Variant"), &Viewport::gui_get_drag_data);
ObjectTypeDB::bind_method(_MD("set_disable_input","disable"), &Viewport::set_disable_input);
ObjectTypeDB::bind_method(_MD("is_input_disabled"), &Viewport::is_input_disabled);
@@ -2493,12 +2629,13 @@ Viewport::Viewport() {
world_2d = Ref<World2D>( memnew( World2D ));
viewport = VisualServer::get_singleton()->viewport_create();
- listener=SpatialSoundServer::get_singleton()->listener_create();
+ internal_listener = SpatialSoundServer::get_singleton()->listener_create();
audio_listener=false;
- listener_2d=SpatialSound2DServer::get_singleton()->listener_create();
+ internal_listener_2d = SpatialSound2DServer::get_singleton()->listener_create();
audio_listener_2d=false;
transparent_bg=false;
parent=NULL;
+ listener=NULL;
camera=NULL;
size_override=false;
size_override_stretch=false;
@@ -2546,8 +2683,8 @@ Viewport::Viewport() {
Viewport::~Viewport() {
VisualServer::get_singleton()->free( viewport );
- SpatialSoundServer::get_singleton()->free(listener);
- SpatialSound2DServer::get_singleton()->free(listener_2d);
+ SpatialSoundServer::get_singleton()->free(internal_listener);
+ SpatialSound2DServer::get_singleton()->free(internal_listener_2d);
if (render_target_texture.is_valid())
render_target_texture->vp=NULL; //so if used, will crash
}
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index afabd499a9..545020dfc7 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -39,6 +39,7 @@
*/
class Camera;
+class Listener;
class Control;
class CanvasItem;
class Panel;
@@ -92,6 +93,9 @@ friend class RenderTargetTexture;
Control *parent_control;
Viewport *parent;
+ Listener *listener;
+ Set<Listener*> listeners;
+
Camera *camera;
Set<Camera*> cameras;
@@ -100,10 +104,10 @@ friend class RenderTargetTexture;
RID current_canvas;
bool audio_listener;
- RID listener;
+ RID internal_listener;
bool audio_listener_2d;
- RID listener_2d;
+ RID internal_listener_2d;
Matrix32 canvas_transform;
Matrix32 global_canvas_transform;
@@ -159,6 +163,7 @@ friend class RenderTargetTexture;
void _propagate_enter_world(Node *p_node);
void _propagate_exit_world(Node *p_node);
+ void _propagate_viewport_notification(Node *p_node, int p_what);
void _update_stretch_transform();
@@ -262,6 +267,13 @@ friend class Control;
Control *_gui_get_focus_owner();
+friend class Listener;
+ void _listener_transform_changed_notify();
+ void _listener_set(Listener* p_listener);
+ bool _listener_add(Listener* p_listener); //true if first
+ void _listener_remove(Listener* p_listener);
+ void _listener_make_next_current(Listener* p_exclude);
+
friend class Camera;
void _camera_transform_changed_notify();
void _camera_set(Camera* p_camera);
@@ -275,7 +287,7 @@ protected:
static void _bind_methods();
public:
-
+ Listener* get_listener() const;
Camera* get_camera() const;
void set_as_audio_listener(bool p_enable);
@@ -361,6 +373,9 @@ public:
bool gui_has_modal_stack() const;
+ Variant gui_get_drag_data() const;
+
+ virtual String get_configuration_warning() const;
Viewport();
~Viewport();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 9930aa07f6..54b4ddca9e 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -126,11 +126,14 @@
#include "scene/main/scene_main_loop.h"
#include "scene/main/resource_preloader.h"
#include "scene/resources/packed_scene.h"
+#include "scene/main/scene_main_loop.h"
#include "scene/resources/surface_tool.h"
#include "scene/resources/mesh_data_tool.h"
#include "scene/resources/scene_preloader.h"
+#include "scene/resources/dynamic_font.h"
+#include "scene/resources/dynamic_font_stb.h"
#include "scene/main/timer.h"
@@ -190,6 +193,7 @@
#ifndef _3D_DISABLED
#include "scene/3d/camera.h"
+#include "scene/3d/listener.h"
#include "scene/3d/interpolated_camera.h"
#include "scene/3d/position_3d.h"
@@ -235,6 +239,8 @@ static ResourceFormatLoaderShader *resource_loader_shader=NULL;
static ResourceFormatSaverText *resource_saver_text=NULL;
static ResourceFormatLoaderText *resource_loader_text=NULL;
+static ResourceFormatLoaderDynamicFont *resource_loader_dynamic_font=NULL;
+
//static SceneStringNames *string_names;
void register_scene_types() {
@@ -250,7 +256,8 @@ void register_scene_types() {
resource_loader_wav = memnew( ResourceFormatLoaderWAV );
ResourceLoader::add_resource_format_loader( resource_loader_wav );
-
+ resource_loader_dynamic_font = memnew( ResourceFormatLoaderDynamicFont );
+ ResourceLoader::add_resource_format_loader( resource_loader_dynamic_font );
#ifdef TOOLS_ENABLED
@@ -287,6 +294,7 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
+ ObjectTypeDB::register_type<ShortCut>();
ObjectTypeDB::register_type<Control>();
// ObjectTypeDB::register_type<EmptyControl>();
ObjectTypeDB::add_compatibility_type("EmptyControl","Control");
@@ -380,6 +388,7 @@ void register_scene_types() {
ObjectTypeDB::register_type<BoneAttachment>();
ObjectTypeDB::register_virtual_type<VisualInstance>();
ObjectTypeDB::register_type<Camera>();
+ ObjectTypeDB::register_type<Listener>();
ObjectTypeDB::register_type<InterpolatedCamera>();
ObjectTypeDB::register_type<TestCube>();
ObjectTypeDB::register_type<MeshInstance>();
@@ -570,13 +579,21 @@ void register_scene_types() {
ObjectTypeDB::register_type<LargeTexture>();
ObjectTypeDB::register_type<CubeMap>();
ObjectTypeDB::register_type<Animation>();
- ObjectTypeDB::register_type<Font>();
+ ObjectTypeDB::register_virtual_type<Font>();
+ ObjectTypeDB::register_type<BitmapFont>();
+
+ ObjectTypeDB::register_type<DynamicFontData>();
+ ObjectTypeDB::register_type<DynamicFont>();
+
ObjectTypeDB::register_type<StyleBoxEmpty>();
ObjectTypeDB::register_type<StyleBoxTexture>();
ObjectTypeDB::register_type<StyleBoxFlat>();
ObjectTypeDB::register_type<StyleBoxImageMask>();
ObjectTypeDB::register_type<Theme>();
+ ObjectTypeDB::add_compatibility_type("Font","BitmapFont");
+
+
ObjectTypeDB::register_type<PolygonPathFinder>();
ObjectTypeDB::register_type<BitMap>();
ObjectTypeDB::register_type<ColorRamp>();
@@ -634,6 +651,8 @@ void unregister_scene_types() {
memdelete( resource_loader_image );
memdelete( resource_loader_wav );
+ memdelete( resource_loader_dynamic_font );
+
#ifdef TOOLS_ENABLED
diff --git a/scene/resources/bit_mask.h b/scene/resources/bit_mask.h
index b245ea1542..e75a2aa332 100644
--- a/scene/resources/bit_mask.h
+++ b/scene/resources/bit_mask.h
@@ -36,6 +36,8 @@
class BitMap : public Resource {
OBJ_TYPE(BitMap,Resource);
+ OBJ_SAVE_TYPE(BitMap);
+ RES_BASE_EXTENSION("pbm");
Vector<uint8_t> bitmask;
int width;
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 7dec4029fc..88ff09e961 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -543,6 +543,7 @@ void Curve2D::_bake() const {
Vector2 pos=points[0].pos;
List<Vector2> pointlist;
+ pointlist.push_back(pos); //start always from origin
for(int i=0;i<points.size()-1;i++) {
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 005a46c0bc..9ebb7e7561 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -28,6 +28,7 @@
typedef Map<const void*,Ref<ImageTexture> > TexCacheMap;
static TexCacheMap *tex_cache;
+static int scale=1;
template<class T>
static Ref<StyleBoxTexture> make_stylebox(T p_src,float p_left, float p_top, float p_right, float p_botton,float p_margin_left=-1, float p_margin_top=-1, float p_margin_right=-1, float p_margin_botton=-1, bool p_draw_center=true) {
@@ -40,21 +41,24 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src,float p_left, float p_top, flo
} else {
texture = Ref<ImageTexture>( memnew( ImageTexture ) );
- texture->create_from_image( Image(p_src),ImageTexture::FLAG_FILTER );
+ Image img(p_src);
+ if (scale>1)
+ img.expand_x2_hq2x();
+ texture->create_from_image( img,ImageTexture::FLAG_FILTER );
(*tex_cache)[p_src]=texture;
}
Ref<StyleBoxTexture> style( memnew( StyleBoxTexture ) );
style->set_texture(texture);
- style->set_margin_size( MARGIN_LEFT, p_left );
- style->set_margin_size( MARGIN_RIGHT, p_right );
- style->set_margin_size( MARGIN_BOTTOM, p_botton );
- style->set_margin_size( MARGIN_TOP, p_top );
- style->set_default_margin( MARGIN_LEFT, p_margin_left );
- style->set_default_margin( MARGIN_RIGHT, p_margin_right );
- style->set_default_margin( MARGIN_BOTTOM, p_margin_botton );
- style->set_default_margin( MARGIN_TOP, p_margin_top );
+ style->set_margin_size( MARGIN_LEFT, p_left * scale);
+ style->set_margin_size( MARGIN_RIGHT, p_right * scale);
+ style->set_margin_size( MARGIN_BOTTOM, p_botton * scale);
+ style->set_margin_size( MARGIN_TOP, p_top * scale);
+ style->set_default_margin( MARGIN_LEFT, p_margin_left * scale);
+ style->set_default_margin( MARGIN_RIGHT, p_margin_right * scale);
+ style->set_default_margin( MARGIN_BOTTOM, p_margin_botton * scale);
+ style->set_default_margin( MARGIN_TOP, p_margin_top * scale);
style->set_draw_center(p_draw_center);
return style;
@@ -63,10 +67,10 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src,float p_left, float p_top, flo
static Ref<StyleBoxTexture> sb_expand(Ref<StyleBoxTexture> p_sbox,float p_left, float p_top, float p_right, float p_botton) {
- p_sbox->set_expand_margin_size(MARGIN_LEFT,p_left);
- p_sbox->set_expand_margin_size(MARGIN_TOP,p_top);
- p_sbox->set_expand_margin_size(MARGIN_RIGHT,p_right);
- p_sbox->set_expand_margin_size(MARGIN_BOTTOM,p_botton);
+ p_sbox->set_expand_margin_size(MARGIN_LEFT,p_left * scale);
+ p_sbox->set_expand_margin_size(MARGIN_TOP,p_top * scale);
+ p_sbox->set_expand_margin_size(MARGIN_RIGHT,p_right * scale);
+ p_sbox->set_expand_margin_size(MARGIN_BOTTOM,p_botton * scale);
return p_sbox;
}
@@ -75,7 +79,10 @@ static Ref<Texture> make_icon(T p_src) {
Ref<ImageTexture> texture( memnew( ImageTexture ) );
- texture->create_from_image( Image(p_src),ImageTexture::FLAG_FILTER );
+ Image img = Image(p_src);
+ if (scale>1)
+ img.expand_x2_hq2x();
+ texture->create_from_image( img,ImageTexture::FLAG_FILTER );
return texture;
}
@@ -87,10 +94,10 @@ static Ref<Shader> make_shader(const char*vertex_code,const char*fragment_code,c
return shader;
}
-static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
+static Ref<BitmapFont> make_font(int p_height,int p_ascent, int p_valign, int p_charcount, const int *p_chars,const Ref<Texture> &p_texture) {
- Ref<Font> font( memnew( Font ) );
+ Ref<BitmapFont> font( memnew( BitmapFont ) );
font->add_texture( p_texture );
for (int i=0;i<p_charcount;i++) {
@@ -117,10 +124,10 @@ static Ref<Font> make_font(int p_height,int p_ascent, int p_valign, int p_charco
return font;
}
-static Ref<Font> make_font2(int p_height,int p_ascent, int p_charcount, const int *p_char_rects,int p_kerning_count,const int *p_kernings,int p_w, int p_h, const unsigned char *p_img) {
+static Ref<BitmapFont> make_font2(int p_height,int p_ascent, int p_charcount, const int *p_char_rects,int p_kerning_count,const int *p_kernings,int p_w, int p_h, const unsigned char *p_img) {
- Ref<Font> font( memnew( Font ) );
+ Ref<BitmapFont> font( memnew( BitmapFont ) );
DVector<uint8_t> img;
img.resize(p_w*p_h*2);
@@ -170,27 +177,24 @@ static Ref<StyleBox> make_empty_stylebox(float p_margin_left=-1, float p_margin_
Ref<StyleBox> style( memnew( StyleBoxEmpty) );
- style->set_default_margin( MARGIN_LEFT, p_margin_left );
- style->set_default_margin( MARGIN_RIGHT, p_margin_right );
- style->set_default_margin( MARGIN_BOTTOM, p_margin_botton );
- style->set_default_margin( MARGIN_TOP, p_margin_top );
+ style->set_default_margin( MARGIN_LEFT, p_margin_left * scale);
+ style->set_default_margin( MARGIN_RIGHT, p_margin_right * scale);
+ style->set_default_margin( MARGIN_BOTTOM, p_margin_botton * scale);
+ style->set_default_margin( MARGIN_TOP, p_margin_top * scale);
return style;
}
-#ifndef DEFAULT_THEME_DISABLED
-
-void make_default_theme() {
+void fill_default_theme(Ref<Theme>& t,const Ref<Font> & default_font,const Ref<Font> & large_font,Ref<Texture>& default_icon, Ref<StyleBox>& default_style,bool p_hidpi) {
+ if (p_hidpi)
+ scale=2;
+ else
+ scale=1;
tex_cache = memnew( TexCacheMap );
- Ref<Theme> t( memnew( Theme ) );
-
- //Ref<Font> default_font = make_font(_bi_font_normal_height,_bi_font_normal_ascent,_bi_font_normal_valign,_bi_font_normal_charcount,_bi_font_normal_characters,make_icon(font_normal_png));
- Ref<Font> default_font=make_font2(_builtin_normal_font_height,_builtin_normal_font_ascent,_builtin_normal_font_charcount,&_builtin_normal_font_charrects[0][0],_builtin_normal_font_kerning_pair_count,&_builtin_normal_font_kerning_pairs[0][0],_builtin_normal_font_img_width,_builtin_normal_font_img_height,_builtin_normal_font_img_data);
- Ref<Font> source_font=make_font2(_builtin_source_font_height,_builtin_source_font_ascent,_builtin_source_font_charcount,&_builtin_source_font_charrects[0][0],_builtin_source_font_kerning_pair_count,&_builtin_source_font_kerning_pairs[0][0],_builtin_source_font_img_width,_builtin_source_font_img_height,_builtin_source_font_img_data);
- Ref<Font> large_font=make_font2(_builtin_large_font_height,_builtin_large_font_ascent,_builtin_large_font_charcount,&_builtin_large_font_charrects[0][0],_builtin_large_font_kerning_pair_count,&_builtin_large_font_kerning_pairs[0][0],_builtin_large_font_img_width,_builtin_large_font_img_height,_builtin_large_font_img_data);
+ //Ref<BitmapFont> default_font = make_font(_bi_font_normal_height,_bi_font_normal_ascent,_bi_font_normal_valign,_bi_font_normal_charcount,_bi_font_normal_characters,make_icon(font_normal_png));
// Font Colors
@@ -213,7 +217,7 @@ void make_default_theme() {
Ref<StyleBoxTexture> focus = make_stylebox( focus_png,5,5,5,5);
for(int i=0;i<4;i++) {
- focus->set_expand_margin_size(Margin(i),1);
+ focus->set_expand_margin_size(Margin(i),1 *scale);
}
@@ -239,7 +243,7 @@ void make_default_theme() {
t->set_color("font_color_hover","Button", control_font_color_hover );
t->set_color("font_color_disabled","Button", control_font_color_disabled );
- t->set_constant("hseparation","Button", 2);
+ t->set_constant("hseparation","Button", 2 *scale);
// LinkButton
@@ -249,7 +253,7 @@ void make_default_theme() {
t->set_color("font_color_pressed","LinkButton", control_font_color_pressed );
t->set_color("font_color_hover","LinkButton", control_font_color_hover );
- t->set_constant("underline_spacing","LinkButton", 2 );
+ t->set_constant("underline_spacing","LinkButton", 2 *scale);
// ColorPickerButton
@@ -266,16 +270,16 @@ void make_default_theme() {
t->set_color("font_color_hover","ColorPickerButton", Color(1,1,1,1) );
t->set_color("font_color_disabled","ColorPickerButton", Color(0.9,0.9,0.9,0.3) );
- t->set_constant("hseparation","ColorPickerButton", 2 );
+ t->set_constant("hseparation","ColorPickerButton", 2 *scale);
// ToolButton
Ref<StyleBox> tb_empty = memnew( StyleBoxEmpty );
- tb_empty->set_default_margin(MARGIN_LEFT,6);
- tb_empty->set_default_margin(MARGIN_RIGHT,6);
- tb_empty->set_default_margin(MARGIN_TOP,4);
- tb_empty->set_default_margin(MARGIN_BOTTOM,4);
+ tb_empty->set_default_margin(MARGIN_LEFT,6 *scale);
+ tb_empty->set_default_margin(MARGIN_RIGHT,6 *scale);
+ tb_empty->set_default_margin(MARGIN_TOP,4 *scale);
+ tb_empty->set_default_margin(MARGIN_BOTTOM,4 *scale);
t->set_stylebox("normal","ToolButton", tb_empty);
t->set_stylebox("pressed","ToolButton", make_stylebox( button_pressed_png,4,4,4,4) );
@@ -316,8 +320,8 @@ void make_default_theme() {
t->set_color("font_color_hover","OptionButton", control_font_color_hover );
t->set_color("font_color_disabled","OptionButton", control_font_color_disabled );
- t->set_constant("hseparation","OptionButton", 2 );
- t->set_constant("arrow_margin","OptionButton", 2 );
+ t->set_constant("hseparation","OptionButton", 2 *scale);
+ t->set_constant("arrow_margin","OptionButton", 2 *scale);
@@ -336,7 +340,7 @@ void make_default_theme() {
t->set_color("font_color_hover","MenuButton", control_font_color_hover );
t->set_color("font_color_disabled","MenuButton", Color(1,1,1,0.3) );
- t->set_constant("hseparation","MenuButton", 3 );
+ t->set_constant("hseparation","MenuButton", 3 *scale);
// ButtonGroup
@@ -345,15 +349,15 @@ void make_default_theme() {
// CheckBox
Ref<StyleBox> cbx_empty = memnew( StyleBoxEmpty );
- cbx_empty->set_default_margin(MARGIN_LEFT,22);
- cbx_empty->set_default_margin(MARGIN_RIGHT,4);
- cbx_empty->set_default_margin(MARGIN_TOP,4);
- cbx_empty->set_default_margin(MARGIN_BOTTOM,5);
+ cbx_empty->set_default_margin(MARGIN_LEFT,22 *scale);
+ cbx_empty->set_default_margin(MARGIN_RIGHT,4 *scale);
+ cbx_empty->set_default_margin(MARGIN_TOP,4 *scale);
+ cbx_empty->set_default_margin(MARGIN_BOTTOM,5 *scale);
Ref<StyleBox> cbx_focus = focus;
- cbx_focus->set_default_margin(MARGIN_LEFT,4);
- cbx_focus->set_default_margin(MARGIN_RIGHT,22);
- cbx_focus->set_default_margin(MARGIN_TOP,4);
- cbx_focus->set_default_margin(MARGIN_BOTTOM,5);
+ cbx_focus->set_default_margin(MARGIN_LEFT,4 *scale);
+ cbx_focus->set_default_margin(MARGIN_RIGHT,22 *scale);
+ cbx_focus->set_default_margin(MARGIN_TOP,4 *scale);
+ cbx_focus->set_default_margin(MARGIN_BOTTOM,5 *scale);
t->set_stylebox("normal","CheckBox", cbx_empty );
t->set_stylebox("pressed","CheckBox", cbx_empty );
@@ -373,18 +377,18 @@ void make_default_theme() {
t->set_color("font_color_hover","CheckBox", control_font_color_hover );
t->set_color("font_color_disabled","CheckBox", control_font_color_disabled );
- t->set_constant("hseparation","CheckBox",4);
- t->set_constant("check_vadjust","CheckBox",0);
+ t->set_constant("hseparation","CheckBox",4 *scale);
+ t->set_constant("check_vadjust","CheckBox",0 *scale);
// CheckButton
Ref<StyleBox> cb_empty = memnew( StyleBoxEmpty );
- cb_empty->set_default_margin(MARGIN_LEFT,6);
- cb_empty->set_default_margin(MARGIN_RIGHT,70);
- cb_empty->set_default_margin(MARGIN_TOP,4);
- cb_empty->set_default_margin(MARGIN_BOTTOM,4);
+ cb_empty->set_default_margin(MARGIN_LEFT,6 *scale);
+ cb_empty->set_default_margin(MARGIN_RIGHT,70 *scale);
+ cb_empty->set_default_margin(MARGIN_TOP,4 *scale);
+ cb_empty->set_default_margin(MARGIN_BOTTOM,4 *scale);
t->set_stylebox("normal","CheckButton", cb_empty );
t->set_stylebox("pressed","CheckButton", cb_empty );
@@ -402,8 +406,8 @@ void make_default_theme() {
t->set_color("font_color_hover","CheckButton", control_font_color_hover );
t->set_color("font_color_disabled","CheckButton", control_font_color_disabled );
- t->set_constant("hseparation","CheckButton",4);
- t->set_constant("check_vadjust","CheckButton",0);
+ t->set_constant("hseparation","CheckButton",4 *scale);
+ t->set_constant("check_vadjust","CheckButton",0 *scale);
@@ -414,9 +418,10 @@ void make_default_theme() {
t->set_color("font_color","Label", Color(1,1,1) );
t->set_color("font_color_shadow","Label", Color(0,0,0,0) );
- t->set_constant("shadow_offset_x","Label", 1 );
- t->set_constant("shadow_offset_y","Label", 1 );
- t->set_constant("shadow_as_outline","Label", 0 );
+ t->set_constant("shadow_offset_x","Label", 1 *scale);
+ t->set_constant("shadow_offset_y","Label", 1 *scale);
+ t->set_constant("shadow_as_outline","Label", 0 *scale);
+ t->set_constant("line_spacing","Label", 3 *scale);
@@ -433,7 +438,7 @@ void make_default_theme() {
t->set_color("cursor_color","LineEdit", control_font_color_hover );
t->set_color("selection_color","LineEdit", font_color_selection );
- t->set_constant("minimum_spaces","LineEdit", 12 );
+ t->set_constant("minimum_spaces","LineEdit", 12 *scale);
@@ -467,14 +472,14 @@ void make_default_theme() {
t->set_color("mark_color","TextEdit", Color(1.0,0.4,0.4,0.4) );
t->set_color("breakpoint_color","TextEdit", Color(0.8,0.8,0.4,0.2) );
t->set_color("current_line_color","TextEdit", Color(0.25,0.25,0.26,0.8) );
- t->set_color("cursor_color","TextEdit", control_font_color );
+ t->set_color("caret_color","TextEdit", control_font_color );
t->set_color("symbol_color","TextEdit", control_font_color_hover );
t->set_color("brace_mismatch_color","TextEdit", Color(1,0.2,0.2) );
t->set_constant("completion_lines","TextEdit", 7 );
t->set_constant("completion_max_width","TextEdit", 50 );
t->set_constant("completion_scroll_width","TextEdit", 3 );
- t->set_constant("line_spacing","TextEdit",4 );
+ t->set_constant("line_spacing","TextEdit",4 *scale);
Ref<Texture> empty_icon = memnew( ImageTexture );
@@ -554,10 +559,10 @@ void make_default_theme() {
t->set_color("title_color","WindowDialog", Color(0,0,0) );
- t->set_constant("close_h_ofs","WindowDialog", 22 );
- t->set_constant("close_v_ofs","WindowDialog", 20 );
- t->set_constant("titlebar_height","WindowDialog", 18 );
- t->set_constant("title_height","WindowDialog", 20 );
+ t->set_constant("close_h_ofs","WindowDialog", 22 *scale);
+ t->set_constant("close_v_ofs","WindowDialog", 20 *scale);
+ t->set_constant("titlebar_height","WindowDialog", 18 *scale);
+ t->set_constant("title_height","WindowDialog", 20 *scale);
// File Dialog
@@ -571,7 +576,7 @@ void make_default_theme() {
Ref<StyleBoxTexture> selected = make_stylebox( selection_png,6,6,6,6);
for(int i=0;i<4;i++) {
- selected->set_expand_margin_size(Margin(i),2);
+ selected->set_expand_margin_size(Margin(i),2 *scale);
}
t->set_stylebox("panel","PopupPanel", style_pp );
@@ -597,8 +602,8 @@ void make_default_theme() {
t->set_color("font_color_disabled","PopupMenu", Color(0.4,0.4,0.4,0.8) );
t->set_color("font_color_hover","PopupMenu", control_font_color );
- t->set_constant("hseparation","PopupMenu",4);
- t->set_constant("vseparation","PopupMenu",4);
+ t->set_constant("hseparation","PopupMenu",4 *scale);
+ t->set_constant("vseparation","PopupMenu",4 *scale);
// GraphNode
@@ -613,14 +618,14 @@ void make_default_theme() {
t->set_stylebox("selectedframe","GraphNode", graphsbselected );
t->set_stylebox("defaultframe", "GraphNode", graphsbdefault );
t->set_stylebox("defaultfocus", "GraphNode", graphsbdeffocus );
- t->set_constant("separation","GraphNode", 1 );
+ t->set_constant("separation","GraphNode", 1 *scale);
t->set_icon("port","GraphNode", make_icon( graph_port_png ) );
t->set_icon("close","GraphNode", make_icon( graph_node_close_png ) );
t->set_font("title_font","GraphNode", default_font );
t->set_color("title_color","GraphNode", Color(0,0,0,1));
- t->set_constant("title_offset","GraphNode", 18);
- t->set_constant("close_offset","GraphNode", 18);
- t->set_constant("port_offset","GraphNode", 3);
+ t->set_constant("title_offset","GraphNode", 18 *scale);
+ t->set_constant("close_offset","GraphNode", 18 *scale);
+ t->set_constant("port_offset","GraphNode", 3 *scale);
// Tree
@@ -655,12 +660,13 @@ void make_default_theme() {
t->set_color("selection_color","Tree", Color(0.1,0.1,1,0.8) );
t->set_color("cursor_color","Tree", Color(0,0,0) );
t->set_color("guide_color","Tree", Color(0,0,0,0.1) );
+ t->set_color("drop_position_color","Tree", Color(1,0.3,0.2) );
- t->set_constant("hseparation","Tree",4);
- t->set_constant("vseparation","Tree",4);
- t->set_constant("guide_width","Tree",2);
- t->set_constant("item_margin","Tree",12);
- t->set_constant("button_margin","Tree",4);
+ t->set_constant("hseparation","Tree",4 *scale);
+ t->set_constant("vseparation","Tree",4 *scale);
+ t->set_constant("guide_width","Tree",2 *scale);
+ t->set_constant("item_margin","Tree",12 *scale);
+ t->set_constant("button_margin","Tree",4 *scale);
// ItemList
@@ -672,7 +678,7 @@ void make_default_theme() {
t->set_constant("hseparation","ItemList",4);
t->set_constant("vseparation","ItemList",2);
t->set_constant("icon_margin","ItemList",4);
- t->set_constant("line_separation","ItemList",2);
+ t->set_constant("line_separation","ItemList",2 *scale);
t->set_font("font","ItemList", default_font );
t->set_color("font_color","ItemList", control_font_color_lower );
t->set_color("font_color_selected","ItemList", control_font_color_pressed );
@@ -693,8 +699,8 @@ void make_default_theme() {
Ref<StyleBoxTexture> tc_sb = sb_expand( make_stylebox( tab_container_bg_png,4,4,4,4,4,4,4,4),3,3,3,3);
- tc_sb->set_expand_margin_size(MARGIN_TOP,2);
- tc_sb->set_default_margin(MARGIN_TOP,8);
+ tc_sb->set_expand_margin_size(MARGIN_TOP,2 *scale);
+ tc_sb->set_default_margin(MARGIN_TOP,8 *scale);
t->set_stylebox("tab_fg","TabContainer", sb_expand( make_stylebox( tab_current_png,4,4,4,1,16,4,16,4),2,2,2,2) );
t->set_stylebox("tab_bg","TabContainer", sb_expand( make_stylebox( tab_behind_png,5,5,5,1,16,6,16,4),3,0,3,3) );
@@ -712,11 +718,11 @@ void make_default_theme() {
t->set_color("font_color_fg","TabContainer", control_font_color_hover );
t->set_color("font_color_bg","TabContainer", control_font_color_low );
- t->set_constant("side_margin","TabContainer", 8 );
- t->set_constant("top_margin","TabContainer", 24);
- t->set_constant("label_valign_fg","TabContainer", 0);
- t->set_constant("label_valign_bg","TabContainer", 2);
- t->set_constant("hseparation","TabContainer", 4);
+ t->set_constant("side_margin","TabContainer", 8 *scale);
+ t->set_constant("top_margin","TabContainer", 24 *scale);
+ t->set_constant("label_valign_fg","TabContainer", 0 *scale);
+ t->set_constant("label_valign_bg","TabContainer", 2 *scale);
+ t->set_constant("hseparation","TabContainer", 4 *scale);
@@ -739,10 +745,10 @@ void make_default_theme() {
t->set_color("font_color_fg","Tabs", control_font_color_hover );
t->set_color("font_color_bg","Tabs", control_font_color_low );
- t->set_constant("top_margin","Tabs", 24);
- t->set_constant("label_valign_fg","Tabs", 0);
- t->set_constant("label_valign_bg","Tabs", 2);
- t->set_constant("hseparation","Tabs", 4);
+ t->set_constant("top_margin","Tabs", 24 *scale);
+ t->set_constant("label_valign_fg","Tabs", 0 *scale);
+ t->set_constant("label_valign_bg","Tabs", 2 *scale);
+ t->set_constant("hseparation","Tabs", 4 *scale);
@@ -752,18 +758,17 @@ void make_default_theme() {
t->set_stylebox("separator","VSeparator", make_stylebox( hseparator_png,3,3,3,3) );
t->set_icon("close","Icons", make_icon(icon_close_png));
- t->set_font("source","Fonts", source_font);
t->set_font("normal","Fonts", default_font );
t->set_font("large","Fonts", large_font );
- t->set_constant("separation","HSeparator", 4);
- t->set_constant("separation","VSeparator", 4);
+ t->set_constant("separation","HSeparator", 4 *scale);
+ t->set_constant("separation","VSeparator", 4 *scale);
// Dialogs
- t->set_constant("margin","Dialogs",8);
- t->set_constant("button_margin","Dialogs",32);
+ t->set_constant("margin","Dialogs",8 *scale);
+ t->set_constant("button_margin","Dialogs",32 *scale);
@@ -776,11 +781,11 @@ void make_default_theme() {
// colorPicker
- t->set_constant("value_height","ColorPicker", 23 );
- t->set_constant("value_width","ColorPicker", 50);
- t->set_constant("color_width","ColorPicker", 100);
- t->set_constant("label_width","ColorPicker", 20);
- t->set_constant("hseparator","ColorPicker", 4);
+ t->set_constant("value_height","ColorPicker", 23 *scale);
+ t->set_constant("value_width","ColorPicker", 50 *scale);
+ t->set_constant("color_width","ColorPicker", 100 *scale);
+ t->set_constant("label_width","ColorPicker", 20 *scale);
+ t->set_constant("hseparator","ColorPicker", 4 *scale);
t->set_icon("screen_picker","ColorPicker", make_icon( icon_color_pick_png ) );
t->set_icon("add_preset","ColorPicker", make_icon( icon_add_png ) );
@@ -792,7 +797,7 @@ void make_default_theme() {
Ref<StyleBoxTexture> style_tt = make_stylebox( tooltip_bg_png,4,4,4,4);
for(int i=0;i<4;i++)
- style_tt->set_expand_margin_size((Margin)i,4);
+ style_tt->set_expand_margin_size((Margin)i,4 *scale);
t->set_stylebox("panel","TooltipPanel", style_tt );
@@ -820,9 +825,9 @@ void make_default_theme() {
t->set_color("font_color_selected","RichTextLabel", font_color_selection );
t->set_color("selection_color","RichTextLabel", Color(0.1,0.1,1,0.8) );
- t->set_constant("line_separation","RichTextLabel", 1 );
- t->set_constant("table_hseparation","RichTextLabel", 3 );
- t->set_constant("table_vseparation","RichTextLabel", 3 );
+ t->set_constant("line_separation","RichTextLabel", 1 *scale);
+ t->set_constant("table_hseparation","RichTextLabel", 3 *scale);
+ t->set_constant("table_vseparation","RichTextLabel", 3 *scale);
@@ -834,15 +839,18 @@ void make_default_theme() {
t->set_icon("grabber","VSplitContainer",make_icon(vsplitter_png));
t->set_icon("grabber","HSplitContainer",make_icon(hsplitter_png));
- t->set_constant("separation","HBoxContainer",4);
- t->set_constant("separation","VBoxContainer",4);
- t->set_constant("margin","MarginContainer",8);
- t->set_constant("hseparation","GridContainer",4);
- t->set_constant("vseparation","GridContainer",4);
- t->set_constant("separation","HSplitContainer",12);
- t->set_constant("separation","VSplitContainer",12);
- t->set_constant("autohide","HSplitContainer",1);
- t->set_constant("autohide","VSplitContainer",1);
+ t->set_constant("separation","HBoxContainer",4 *scale);
+ t->set_constant("separation","VBoxContainer",4 *scale);
+ t->set_constant("margin_left","MarginContainer",8 *scale);
+ t->set_constant("margin_top","MarginContainer",0 *scale);
+ t->set_constant("margin_right","MarginContainer",0 *scale);
+ t->set_constant("margin_bottom","MarginContainer",0 *scale);
+ t->set_constant("hseparation","GridContainer",4 *scale);
+ t->set_constant("vseparation","GridContainer",4 *scale);
+ t->set_constant("separation","HSplitContainer",12 *scale);
+ t->set_constant("separation","VSplitContainer",12 *scale);
+ t->set_constant("autohide","HSplitContainer",1 *scale);
+ t->set_constant("autohide","VSplitContainer",1 *scale);
@@ -858,8 +866,8 @@ void make_default_theme() {
t->set_color("font_color","HButtonArray", control_font_color_low );
t->set_color("font_color_selected","HButtonArray", control_font_color_hover );
- t->set_constant("icon_separator","HButtonArray", 4 );
- t->set_constant("button_separator","HButtonArray", 8 );
+ t->set_constant("icon_separator","HButtonArray", 4 *scale );
+ t->set_constant("button_separator","HButtonArray", 8 *scale );
t->set_stylebox("focus","HButtonArray", focus );
@@ -876,8 +884,8 @@ void make_default_theme() {
t->set_color("font_color","VButtonArray", control_font_color_low );
t->set_color("font_color_selected","VButtonArray", control_font_color_hover );
- t->set_constant("icon_separator","VButtonArray", 4);
- t->set_constant("button_separator","VButtonArray", 8);
+ t->set_constant("icon_separator","VButtonArray", 4 *scale);
+ t->set_constant("button_separator","VButtonArray", 8 *scale);
t->set_stylebox("focus","VButtonArray", focus );
@@ -909,45 +917,31 @@ void make_default_theme() {
// Theme
- Theme::set_default( t );
- Theme::set_default_icon( make_icon(error_icon_png) );
- Theme::set_default_style( make_stylebox( error_icon_png,2,2,2,2) );
- Theme::set_default_font( default_font );
+ default_icon= make_icon(error_icon_png) ;
+ default_style = make_stylebox( error_icon_png,2,2,2,2) ;
memdelete( tex_cache );
}
-#else
-
-#include "error_icon.xpm"
-
void make_default_theme() {
- Ref<Theme> t( memnew( Theme ) );
-
+ Ref<Theme> t;
+ t.instance();
- Image error_img(error_icon_xpm);
- Ref<Texture> texture( memnew( Texture ) );
- texture->create_from_image( error_img );
+ Ref<StyleBox> default_style;
+ Ref<Texture> default_icon;
+ Ref<BitmapFont> default_font=make_font2(_builtin_normal_font_height,_builtin_normal_font_ascent,_builtin_normal_font_charcount,&_builtin_normal_font_charrects[0][0],_builtin_normal_font_kerning_pair_count,&_builtin_normal_font_kerning_pairs[0][0],_builtin_normal_font_img_width,_builtin_normal_font_img_height,_builtin_normal_font_img_data);
+ Ref<BitmapFont> large_font=make_font2(_builtin_large_font_height,_builtin_large_font_ascent,_builtin_large_font_charcount,&_builtin_large_font_charrects[0][0],_builtin_large_font_kerning_pair_count,&_builtin_large_font_kerning_pairs[0][0],_builtin_large_font_img_width,_builtin_large_font_img_height,_builtin_large_font_img_data);
+ fill_default_theme(t,default_font,large_font,default_icon,default_style,false);
- Ref<StyleBoxTexture> style( memnew( StyleBoxTexture ) );
- style->set_texture(texture);
-
- for(int i=0;i<4;i++) {
- style->set_margin_size( Margin(),8);
- style->set_default_margin( Margin(),8);
- }
-
- Ref<Font> f = make_default_font();
Theme::set_default( t );
- Theme::set_default_icon( texture );
- Theme::set_default_style( style );
- Theme::set_default_font( f );
+ Theme::set_default_icon( default_icon );
+ Theme::set_default_style( default_style );
+ Theme::set_default_font( default_font );
}
-#endif
void clear_default_theme() {
Theme::set_default( Ref<Theme>() );
diff --git a/scene/resources/default_theme/default_theme.h b/scene/resources/default_theme/default_theme.h
index 44569ba192..1e3b4b4081 100644
--- a/scene/resources/default_theme/default_theme.h
+++ b/scene/resources/default_theme/default_theme.h
@@ -12,10 +12,12 @@
#ifndef DEFAULT_THEME_H
#define DEFAULT_THEME_H
+#include "scene/resources/theme.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
+void fill_default_theme(Ref<Theme>& theme,const Ref<Font> & default_font,const Ref<Font> & large_font,Ref<Texture>& default_icon, Ref<StyleBox>& default_style,bool p_hidpi);
void make_default_theme();
void clear_default_theme();
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
new file mode 100644
index 0000000000..78a5571bf0
--- /dev/null
+++ b/scene/resources/dynamic_font.cpp
@@ -0,0 +1,813 @@
+#ifdef FREETYPE_ENABLED
+#include "dynamic_font.h"
+#include "os/file_access.h"
+
+
+
+Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) {
+
+
+ if (size_cache.has(p_size)) {
+ return Ref<DynamicFontAtSize>( size_cache[p_size] );
+ }
+
+
+ Ref<DynamicFontAtSize> dfas;
+
+ dfas.instance();
+
+ dfas->font=Ref<DynamicFontData>( this );
+
+ size_cache[p_size]=dfas.ptr();
+
+ dfas->size=p_size;
+ dfas->_load();
+
+ return dfas;
+
+}
+
+void DynamicFontData::set_font_ptr(const uint8_t* p_font_mem,int p_font_mem_size) {
+
+ font_mem=p_font_mem;
+ font_mem_size=p_font_mem_size;
+}
+
+void DynamicFontData::set_font_path(const String& p_path) {
+
+ font_path=p_path;
+}
+
+void DynamicFontData::set_force_autohinter(bool p_force) {
+
+ force_autohinter=p_force;
+}
+
+DynamicFontData::DynamicFontData()
+{
+
+ force_autohinter=false;
+ font_mem=NULL;
+ font_mem_size=0;
+}
+
+DynamicFontData::~DynamicFontData()
+{
+
+}
+
+
+
+////////////////////
+
+Error DynamicFontAtSize::_load() {
+
+
+ int error = FT_Init_FreeType( &library );
+
+ ERR_EXPLAIN(TTR("Error initializing FreeType."));
+ ERR_FAIL_COND_V( error !=0, ERR_CANT_CREATE );
+
+ if (font->font_path!=String()) {
+
+ FileAccess *f=FileAccess::open(font->font_path,FileAccess::READ);
+ ERR_FAIL_COND_V(!f,ERR_CANT_OPEN);
+
+ memset(&stream,0,sizeof(FT_StreamRec));
+ stream.base=NULL;
+ stream.size=f->get_len();
+ stream.pos=0;
+ stream.descriptor.pointer=f;
+ stream.read=_ft_stream_io;
+ stream.close=_ft_stream_close;
+
+ FT_Open_Args fargs;
+ memset(&fargs,0,sizeof(FT_Open_Args));
+ fargs.flags=FT_OPEN_STREAM;
+ fargs.stream=&stream;
+ error = FT_Open_Face( library,&fargs,0,&face);
+ } else if (font->font_mem) {
+
+ memset(&stream,0,sizeof(FT_StreamRec));
+ stream.base=(unsigned char*)font->font_mem;
+ stream.size=font->font_mem_size;
+ stream.pos=0;
+
+ FT_Open_Args fargs;
+ memset(&fargs,0,sizeof(FT_Open_Args));
+ fargs.memory_base=(unsigned char*)font->font_mem;
+ fargs.memory_size=font->font_mem_size;
+ fargs.flags= FT_OPEN_MEMORY;
+ fargs.stream=&stream;
+ error = FT_Open_Face( library,&fargs,0,&face);
+
+ } else {
+ ERR_EXPLAIN("DynamicFont uninitialized");
+ ERR_FAIL_V(ERR_UNCONFIGURED);
+ }
+
+ //error = FT_New_Face( library, src_path.utf8().get_data(),0,&face );
+
+ if ( error == FT_Err_Unknown_File_Format ) {
+ ERR_EXPLAIN(TTR("Unknown font format."));
+ FT_Done_FreeType( library );
+
+ } else if ( error ) {
+
+ ERR_EXPLAIN(TTR("Error loading font."));
+ FT_Done_FreeType( library );
+
+ }
+
+ ERR_FAIL_COND_V(error,ERR_FILE_CANT_OPEN);
+
+
+ /*error = FT_Set_Char_Size(face,0,64*size,512,512);
+
+ if ( error ) {
+ FT_Done_FreeType( library );
+ ERR_EXPLAIN(TTR("Invalid font size."));
+ ERR_FAIL_COND_V( error, ERR_INVALID_PARAMETER );
+ }*/
+
+ error = FT_Set_Pixel_Sizes(face,0,size);
+
+ ascent=face->size->metrics.ascender>>6;
+ descent=-face->size->metrics.descender>>6;
+ linegap=0;
+
+ //print_line("ASCENT: "+itos(ascent)+" descent "+itos(descent)+" hinted: "+itos(face->face_flags&FT_FACE_FLAG_HINTER));
+
+ valid=true;
+ return OK;
+}
+
+float DynamicFontAtSize::get_height() const {
+
+ return ascent+descent;
+}
+
+float DynamicFontAtSize::get_ascent() const {
+
+ return ascent;
+}
+float DynamicFontAtSize::get_descent() const {
+
+ return descent;
+}
+
+Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next,const Vector<Ref<DynamicFontAtSize> >& p_fallbacks) const {
+
+ if (!valid)
+ return Size2(1,1);
+ const_cast<DynamicFontAtSize*>(this)->_update_char(p_char);
+
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(!c,Size2());
+
+ Size2 ret(0,get_height());
+
+ if (!c->found) {
+
+ //not found, try in fallbacks
+ for(int i=0;i<p_fallbacks.size();i++) {
+
+ DynamicFontAtSize *fb = const_cast<DynamicFontAtSize*>(p_fallbacks[i].ptr());
+ if (!fb->valid)
+ continue;
+
+ fb->_update_char(p_char);
+ const Character *ch = fb->char_map.getptr(p_char);
+ ERR_CONTINUE(!ch);
+
+ if (!ch->found)
+ continue;
+
+ c=ch;
+ break;
+ }
+ //not found, try 0xFFFD to display 'not found'.
+
+ if (!c->found) {
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(0xFFFD);
+ c = char_map.getptr(0xFFFD);
+ ERR_FAIL_COND_V(!c,Size2());
+
+ }
+ }
+
+ if (c->found) {
+ ret.x=c->advance;
+ }
+
+
+ if (p_next) {
+ FT_Vector delta;
+ FT_Get_Kerning( face, p_char,p_next, FT_KERNING_DEFAULT, &delta );
+
+ if (delta.x==0) {
+ for(int i=0;i<p_fallbacks.size();i++) {
+
+ DynamicFontAtSize *fb = const_cast<DynamicFontAtSize*>(p_fallbacks[i].ptr());
+ if (!fb->valid)
+ continue;
+
+ FT_Get_Kerning( fb->face, p_char,p_next, FT_KERNING_DEFAULT, &delta );
+
+ if (delta.x==0)
+ continue;
+
+ ret.x+=delta.x>>6;
+ break;
+ }
+ } else {
+ ret.x+=delta.x>>6;
+ }
+
+
+ }
+
+ return ret;
+}
+
+
+float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate,const Vector<Ref<DynamicFontAtSize> >& p_fallbacks) const {
+
+ if (!valid)
+ return 0;
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(p_char);
+
+ const Character * c = char_map.getptr(p_char);
+
+ float advance=0;
+
+ if (!c->found) {
+
+ //not found, try in fallbacks
+ bool used_fallback=false;
+
+ for(int i=0;i<p_fallbacks.size();i++) {
+
+ DynamicFontAtSize *fb = const_cast<DynamicFontAtSize*>(p_fallbacks[i].ptr());
+ if (!fb->valid)
+ continue;
+
+ fb->_update_char(p_char);
+ const Character *ch = fb->char_map.getptr(p_char);
+ ERR_CONTINUE(!ch);
+
+ if (!ch->found)
+ continue;
+
+ Point2 cpos=p_pos;
+ cpos.x+=ch->h_align;
+ cpos.y-=get_ascent();
+ cpos.y+=ch->v_align;
+ ERR_FAIL_COND_V( ch->texture_idx<-1 || ch->texture_idx>=fb->textures.size(),0);
+ if (ch->texture_idx!=-1)
+ VisualServer::get_singleton()->canvas_item_add_texture_rect_region( p_canvas_item, Rect2( cpos, ch->rect.size ), fb->textures[ch->texture_idx].texture->get_rid(),ch->rect, p_modulate );
+ advance=ch->advance;
+ used_fallback=true;
+ break;
+ }
+ //not found, try 0xFFFD to display 'not found'.
+
+ if (!used_fallback) {
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(0xFFFD);
+ c = char_map.getptr(0xFFFD);
+
+ }
+ }
+
+ if (c->found) {
+
+
+ Point2 cpos=p_pos;
+ cpos.x+=c->h_align;
+ cpos.y-=get_ascent();
+ cpos.y+=c->v_align;
+ ERR_FAIL_COND_V( c->texture_idx<-1 || c->texture_idx>=textures.size(),0);
+ if (c->texture_idx!=-1)
+ VisualServer::get_singleton()->canvas_item_add_texture_rect_region( p_canvas_item, Rect2( cpos, c->rect.size ), textures[c->texture_idx].texture->get_rid(),c->rect, p_modulate );
+ advance=c->advance;
+ //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
+ }
+
+
+ if (p_next) {
+
+ FT_Vector delta;
+ FT_Get_Kerning( face, p_char,p_next, FT_KERNING_DEFAULT, &delta );
+
+ if (delta.x==0) {
+ for(int i=0;i<p_fallbacks.size();i++) {
+
+ DynamicFontAtSize *fb = const_cast<DynamicFontAtSize*>(p_fallbacks[i].ptr());
+ if (!fb->valid)
+ continue;
+
+ FT_Get_Kerning( fb->face, p_char,p_next, FT_KERNING_DEFAULT, &delta );
+
+ if (delta.x==0)
+ continue;
+
+ advance+=delta.x>>6;
+ break;
+ }
+ } else {
+ advance+=delta.x>>6;
+ }
+ }
+
+ return advance;
+}
+
+unsigned long DynamicFontAtSize::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count ) {
+
+
+ FileAccess *f=(FileAccess*)stream->descriptor.pointer;
+
+ if (f->get_pos()!=offset) {
+ f->seek(offset);
+
+ }
+
+ if (count==0)
+ return 0;
+
+ return f->get_buffer(buffer,count);
+}
+void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) {
+
+ FileAccess *f=(FileAccess*)stream->descriptor.pointer;
+ f->close();
+ memdelete(f);
+}
+
+
+void DynamicFontAtSize::_update_char(CharType p_char) {
+
+ if (char_map.has(p_char))
+ return;
+
+ _THREAD_SAFE_METHOD_
+
+ FT_GlyphSlot slot = face->glyph;
+
+ if (FT_Get_Char_Index( face, p_char)==0) {
+ //not found
+ Character ch;
+ ch.texture_idx=-1;
+ ch.advance=0;
+ ch.h_align=0;
+ ch.v_align=0;
+ ch.found=false;
+
+ char_map[p_char]=ch;
+ return;
+ }
+ int error = FT_Load_Char( face, p_char, FT_LOAD_RENDER|(font->force_autohinter?FT_LOAD_FORCE_AUTOHINT:0) );
+ if (!error) {
+ error = FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+ }
+ if (error) {
+
+ int advance=0;
+ //stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);
+ //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale));
+ Character ch;
+ ch.texture_idx=-1;
+ ch.advance=advance;
+ ch.h_align=0;
+ ch.v_align=0;
+ ch.found=false;
+
+ char_map[p_char]=ch;
+
+
+ return;
+ }
+
+
+
+ int w = slot->bitmap.width;
+ int h = slot->bitmap.rows;
+ int p = slot->bitmap.pitch;
+ int yofs=slot->bitmap_top;
+ int xofs=slot->bitmap_left;
+ int advance=slot->advance.x>>6;
+
+
+ int mw=w+rect_margin*2;
+ int mh=h+rect_margin*2;
+
+ if (mw>4096 || mh>4096) {
+
+ ERR_FAIL_COND(mw>4096);
+ ERR_FAIL_COND(mh>4096);
+ }
+
+ //find a texture to fit this...
+
+ int tex_index=-1;
+ int tex_x=0;
+ int tex_y=0;
+
+ for(int i=0;i<textures.size();i++) {
+
+ CharTexture &ct=textures[i];
+
+ if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture
+ continue;
+
+ tex_y=0x7FFFFFFF;
+ tex_x=0;
+
+ for(int j=0;j<ct.texture_size-mw;j++) {
+
+ int max_y=0;
+
+ for(int k=j;k<j+mw;k++) {
+
+ int y = ct.offsets[k];
+ if (y>max_y)
+ max_y=y;
+ }
+
+ if (max_y<tex_y) {
+ tex_y=max_y;
+ tex_x=j;
+ }
+ }
+
+ if (tex_y==0x7FFFFFFF || tex_y+mh > ct.texture_size)
+ continue; //fail, could not fit it here
+
+ tex_index=i;
+ break;
+ }
+
+// print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y));
+
+ if (tex_index==-1) {
+ //could not find texture to fit, create one
+ tex_x = 0;
+ tex_y = 0;
+
+ int texsize = MAX(size*8,256);
+ if (mw>texsize)
+ texsize=mw; //special case, adapt to it?
+ if (mh>texsize)
+ texsize=mh; //special case, adapt to it?
+
+ texsize=nearest_power_of_2(texsize);
+
+ texsize=MIN(texsize,4096);
+
+
+ CharTexture tex;
+ tex.texture_size=texsize;
+ tex.imgdata.resize(texsize*texsize*2); //grayscale alpha
+
+ {
+ //zero texture
+ DVector<uint8_t>::Write w = tex.imgdata.write();
+ ERR_FAIL_COND(texsize*texsize*2 > tex.imgdata.size());
+ for(int i=0;i<texsize*texsize*2;i++) {
+ w[i]=0;
+ }
+ }
+ tex.offsets.resize(texsize);
+ for(int i=0;i<texsize;i++) //zero offsets
+ tex.offsets[i]=0;
+
+ textures.push_back(tex);
+ tex_index=textures.size()-1;
+
+ }
+
+
+ //fit character in char texture
+
+ CharTexture &tex=textures[tex_index];
+
+ {
+ DVector<uint8_t>::Write wr = tex.imgdata.write();
+
+
+ for(int i=0;i<h;i++) {
+ for(int j=0;j<w;j++) {
+
+ int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2;
+ ERR_FAIL_COND(ofs >= tex.imgdata.size());
+ wr[ofs+0]=255; //grayscale as 1
+ wr[ofs+1]=slot->bitmap.buffer[i*slot->bitmap.width+j];
+ }
+ }
+ }
+
+ //blit to image and texture
+ {
+
+ Image img(tex.texture_size,tex.texture_size,0,Image::FORMAT_GRAYSCALE_ALPHA,tex.imgdata);
+
+ if (tex.texture.is_null()) {
+ tex.texture.instance();
+ tex.texture->create_from_image(img,0/*Texture::FLAG_FILTER*/);
+ } else {
+ tex.texture->set_data(img); //update
+ }
+
+ }
+
+
+ // update height array
+
+ for(int k=tex_x;k<tex_x+mw;k++) {
+
+ tex.offsets[k]=tex_y+mh;
+ }
+
+
+ Character chr;
+ chr.h_align=xofs;
+ chr.v_align=ascent-yofs;// + ascent - descent;
+ chr.advance=advance;
+ chr.texture_idx=tex_index;
+ chr.found=true;
+
+
+ chr.rect=Rect2(tex_x+rect_margin,tex_y+rect_margin,w,h);
+
+ //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs));
+
+ char_map[p_char]=chr;
+
+
+}
+
+DynamicFontAtSize::DynamicFontAtSize() {
+
+ valid=false;
+ rect_margin=1;
+ ascent=1;
+ descent=1;
+ linegap=1;
+}
+
+DynamicFontAtSize::~DynamicFontAtSize(){
+
+ if (valid) {
+ FT_Done_FreeType( library );
+ font->size_cache.erase(size);
+ }
+}
+
+/////////////////////////
+
+
+
+void DynamicFont::set_font_data(const Ref<DynamicFontData>& p_data) {
+
+ data=p_data;
+ data_at_size=data->_get_dynamic_font_at_size(size);
+ emit_changed();
+}
+
+
+Ref<DynamicFontData> DynamicFont::get_font_data() const{
+
+ return data;
+}
+
+void DynamicFont::set_size(int p_size){
+
+ if (size==p_size)
+ return;
+ size=p_size;
+ ERR_FAIL_COND(p_size<1);
+ if (!data.is_valid())
+ return;
+ data_at_size=data->_get_dynamic_font_at_size(size);
+ for(int i=0;i<fallbacks.size();i++) {
+ fallback_data_at_size[i]=fallbacks[i]->_get_dynamic_font_at_size(size);
+ }
+
+ emit_changed();
+ _change_notify();
+}
+int DynamicFont::get_size() const{
+
+ return size;
+}
+
+float DynamicFont::get_height() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_height();
+}
+
+float DynamicFont::get_ascent() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_ascent();
+}
+
+float DynamicFont::get_descent() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_descent();
+
+}
+
+Size2 DynamicFont::get_char_size(CharType p_char,CharType p_next) const{
+
+ if (!data_at_size.is_valid())
+ return Size2(1,1);
+
+ return data_at_size->get_char_size(p_char,p_next,fallback_data_at_size);
+
+}
+
+bool DynamicFont::is_distance_field_hint() const{
+
+ return false;
+}
+
+float DynamicFont::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+
+ if (!data_at_size.is_valid())
+ return 0;
+
+ return data_at_size->draw_char(p_canvas_item,p_pos,p_char,p_next,p_modulate,fallback_data_at_size);
+
+}
+void DynamicFont::set_fallback(int p_idx,const Ref<DynamicFontData>& p_data) {
+
+ ERR_FAIL_COND(p_data.is_null());
+ ERR_FAIL_INDEX(p_idx,fallbacks.size());
+ fallbacks[p_idx]=p_data;
+ fallback_data_at_size[p_idx]=fallbacks[p_idx]->_get_dynamic_font_at_size(size);
+
+}
+
+void DynamicFont::add_fallback(const Ref<DynamicFontData>& p_data) {
+
+ ERR_FAIL_COND(p_data.is_null());
+ fallbacks.push_back(p_data);
+ fallback_data_at_size.push_back(fallbacks[fallbacks.size()-1]->_get_dynamic_font_at_size(size)); //const..
+
+ _change_notify();
+ emit_changed();
+ _change_notify();
+
+}
+
+int DynamicFont::get_fallback_count() const {
+ return fallbacks.size();
+}
+Ref<DynamicFontData> DynamicFont::get_fallback(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,fallbacks.size(),Ref<DynamicFontData>());
+
+ return fallbacks[p_idx];
+}
+void DynamicFont::remove_fallback(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx,fallbacks.size());
+ fallbacks.remove(p_idx);
+ fallback_data_at_size.remove(p_idx);
+ emit_changed();
+ _change_notify();
+}
+
+bool DynamicFont::_set(const StringName& p_name, const Variant& p_value) {
+
+ String str = p_name;
+ if (str.begins_with("fallback/")) {
+ int idx = str.get_slicec('/',1).to_int();
+ Ref<DynamicFontData> fd = p_value;
+
+ if (fd.is_valid()) {
+ if (idx==fallbacks.size()) {
+ add_fallback(fd);
+ return true;
+ } else if (idx>=0 && idx<fallbacks.size()) {
+ set_fallback(idx,fd);
+ return true;
+ } else {
+ return false;
+ }
+ } else if (idx>=0 && idx<fallbacks.size()) {
+ remove_fallback(idx);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool DynamicFont::_get(const StringName& p_name,Variant &r_ret) const{
+
+ String str = p_name;
+ if (str.begins_with("fallback/")) {
+ int idx = str.get_slicec('/',1).to_int();
+
+ if (idx==fallbacks.size()) {
+ r_ret=Ref<DynamicFontData>();
+ return true;
+ } else if (idx>=0 && idx<fallbacks.size()) {
+ r_ret=get_fallback(idx);
+ return true;
+ }
+ }
+
+ return false;
+}
+void DynamicFont::_get_property_list( List<PropertyInfo> *p_list) const{
+
+ for(int i=0;i<fallbacks.size();i++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT,"fallback/"+itos(i),PROPERTY_HINT_RESOURCE_TYPE,"DynamicFontData"));
+ }
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT,"fallback/"+itos(fallbacks.size()),PROPERTY_HINT_RESOURCE_TYPE,"DynamicFontData"));
+}
+
+
+void DynamicFont::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_font_data","data:DynamicFontData"),&DynamicFont::set_font_data);
+ ObjectTypeDB::bind_method(_MD("get_font_data:DynamicFontData"),&DynamicFont::get_font_data);
+
+ ObjectTypeDB::bind_method(_MD("set_size","data"),&DynamicFont::set_size);
+ ObjectTypeDB::bind_method(_MD("get_size"),&DynamicFont::get_size);
+
+ ObjectTypeDB::bind_method(_MD("add_fallback","data:DynamicFontData"),&DynamicFont::add_fallback);
+ ObjectTypeDB::bind_method(_MD("set_fallback","idx","data:DynamicFontData"),&DynamicFont::set_fallback);
+ ObjectTypeDB::bind_method(_MD("get_fallback:DynamicFontData","idx"),&DynamicFont::get_fallback);
+ ObjectTypeDB::bind_method(_MD("remove_fallback","idx"),&DynamicFont::remove_fallback);
+ ObjectTypeDB::bind_method(_MD("get_fallback_count"),&DynamicFont::get_fallback_count);
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"font/size"),_SCS("set_size"),_SCS("get_size"));
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"font/font",PROPERTY_HINT_RESOURCE_TYPE,"DynamicFontData"),_SCS("set_font_data"),_SCS("get_font_data"));
+}
+
+DynamicFont::DynamicFont() {
+
+ size=16;
+}
+
+DynamicFont::~DynamicFont() {
+
+}
+
+/////////////////////////
+
+
+RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_FILE_CANT_OPEN;
+
+ Ref<DynamicFontData> dfont;
+ dfont.instance();;
+ dfont->set_font_path(p_path);
+
+
+ if (r_error)
+ *r_error=OK;
+
+ return dfont;
+}
+
+void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ttf");
+ p_extensions->push_back("otf");
+}
+
+bool ResourceFormatLoaderDynamicFont::handles_type(const String& p_type) const {
+
+ return (p_type=="DynamicFontData");
+}
+
+String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const {
+
+ String el = p_path.extension().to_lower();
+ if (el=="ttf" || el=="otf")
+ return "DynamicFontData";
+ return "";
+}
+
+
+#endif
diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h
new file mode 100644
index 0000000000..1a46e1e468
--- /dev/null
+++ b/scene/resources/dynamic_font.h
@@ -0,0 +1,194 @@
+#ifndef DYNAMIC_FONT_H
+#define DYNAMIC_FONT_H
+
+#ifdef FREETYPE_ENABLED
+#include "scene/resources/font.h"
+#include "os/thread_safe.h"
+#include "io/resource_loader.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+class DynamicFontAtSize;
+class DynamicFont;
+
+class DynamicFontData : public Resource {
+
+ OBJ_TYPE(DynamicFontData,Resource);
+
+
+
+ const uint8_t *font_mem;
+ int font_mem_size;
+ bool force_autohinter;
+
+ String font_path;
+ Map<int,DynamicFontAtSize*> size_cache;
+
+ friend class DynamicFontAtSize;
+
+friend class DynamicFont;
+
+
+ Ref<DynamicFontAtSize> _get_dynamic_font_at_size(int p_size);
+public:
+
+ void set_font_ptr(const uint8_t* p_font_mem,int p_font_mem_size);
+ void set_font_path(const String& p_path);
+ void set_force_autohinter(bool p_force);
+
+ DynamicFontData();
+ ~DynamicFontData();
+};
+
+
+class DynamicFontAtSize : public Reference {
+
+ OBJ_TYPE(DynamicFontAtSize,Reference)
+
+ _THREAD_SAFE_CLASS_
+
+ FT_Library library; /* handle to library */
+ FT_Face face; /* handle to face object */
+ FT_StreamRec stream;
+
+ int ascent;
+ int descent;
+ int linegap;
+ int rect_margin;
+
+ bool valid;
+
+ struct CharTexture {
+
+ DVector<uint8_t> imgdata;
+ int texture_size;
+ Vector<int> offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ Vector<CharTexture> textures;
+
+ struct Character {
+
+ bool found;
+ int texture_idx;
+ Rect2 rect;
+ float v_align;
+ float h_align;
+ float advance;
+
+ Character() { texture_idx=0; v_align=0; }
+ };
+
+
+ static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count );
+ static void _ft_stream_close(FT_Stream stream);
+
+ HashMap< CharType, Character > char_map;
+
+ _FORCE_INLINE_ void _update_char(CharType p_char);
+
+friend class DynamicFontData;
+ Ref<DynamicFontData> font;
+ int size;
+
+
+
+ Error _load();
+protected:
+
+
+
+public:
+
+
+ float get_height() const;
+
+ float get_ascent() const;
+ float get_descent() const;
+
+ Size2 get_char_size(CharType p_char,CharType p_next,const Vector<Ref<DynamicFontAtSize> >& p_fallbacks) const;
+
+ float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate,const Vector<Ref<DynamicFontAtSize> >& p_fallbacks) const;
+
+
+
+ DynamicFontAtSize();
+ ~DynamicFontAtSize();
+};
+
+///////////////
+
+class DynamicFont : public Font {
+
+ OBJ_TYPE( DynamicFont, Font );
+
+ Ref<DynamicFontData> data;
+ Ref<DynamicFontAtSize> data_at_size;
+
+ Vector< Ref<DynamicFontData> > fallbacks;
+ Vector< Ref<DynamicFontAtSize> > fallback_data_at_size;
+
+
+ int size;
+ bool valid;
+
+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_font_data(const Ref<DynamicFontData>& p_data);
+ Ref<DynamicFontData> get_font_data() const;
+
+ void set_size(int p_size);
+ int get_size() const;
+
+
+ void add_fallback(const Ref<DynamicFontData>& p_data);
+ void set_fallback(int p_idx,const Ref<DynamicFontData>& p_data);
+ int get_fallback_count() const;
+ Ref<DynamicFontData> get_fallback(int p_idx) const;
+ void remove_fallback(int p_idx);
+
+ virtual float get_height() const;
+
+ virtual float get_ascent() const;
+ virtual float get_descent() const;
+
+ virtual Size2 get_char_size(CharType p_char,CharType p_next=0) const;
+
+ virtual bool is_distance_field_hint() const;
+
+ virtual float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
+
+ DynamicFont();
+ ~DynamicFont();
+
+};
+
+
+
+/////////////
+
+class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+
+#endif
+
+#endif
diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp
new file mode 100644
index 0000000000..0b9f95da4f
--- /dev/null
+++ b/scene/resources/dynamic_font_stb.cpp
@@ -0,0 +1,527 @@
+#include "dynamic_font_stb.h"
+
+#ifndef FREETYPE_ENABLED
+
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb_truetype.h"
+#include "os/file_access.h"
+
+void DynamicFontData::lock() {
+
+ fr=font_data.read();
+
+ if (fr.ptr()!=last_data_ptr) {
+
+ last_data_ptr=fr.ptr();
+
+ if (!stbtt_InitFont(&info, last_data_ptr, 0)) {
+ valid=false;
+ } else {
+ valid=true;
+ }
+
+ last_data_ptr=fr.ptr();
+ }
+}
+
+void DynamicFontData::unlock() {
+
+ fr = DVector<uint8_t>::Read();
+}
+
+void DynamicFontData::set_font_data(const DVector<uint8_t>& p_font) {
+ //clear caches and stuff
+ ERR_FAIL_COND(font_data.size()) ;
+ font_data=p_font;
+
+ lock();
+
+ if (valid) {
+ stbtt_GetFontVMetrics(&info, &ascent, &descent, &linegap);
+ descent=-descent + linegap;
+
+ for(int i=32;i<1024;i++) {
+ for(int j=32;j<1024;j++) {
+
+ int kern = stbtt_GetCodepointKernAdvance(&info, i,j);
+ if (kern!=0) {
+ KerningPairKey kpk;
+ kpk.A=i;
+ kpk.B=j;
+ kerning_map[kpk]=kern;
+ }
+ }
+ }
+ }
+
+ unlock();
+ //clear existing stuff
+
+ ERR_FAIL_COND(!valid);
+}
+
+Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) {
+
+ ERR_FAIL_COND_V(!valid,Ref<DynamicFontAtSize>());
+
+ if (size_cache.has(p_size)) {
+ return Ref<DynamicFontAtSize>( size_cache[p_size] );
+ }
+
+
+ Ref<DynamicFontAtSize> dfas;
+ dfas.instance();
+
+ dfas->font=Ref<DynamicFontData>( this );
+
+ size_cache[p_size]=dfas.ptr();
+
+ dfas->size=p_size;
+
+ lock();
+
+ dfas->scale = stbtt_ScaleForPixelHeight(&info, p_size);
+
+ unlock();
+
+ return dfas;
+
+}
+
+DynamicFontData::DynamicFontData()
+{
+ last_data_ptr=NULL;
+ valid=false;
+}
+
+DynamicFontData::~DynamicFontData()
+{
+
+}
+
+
+
+////////////////////
+
+float DynamicFontAtSize::get_height() const {
+
+ return (font->ascent+font->descent)*scale;
+}
+
+float DynamicFontAtSize::get_ascent() const {
+
+ return font->ascent*scale;
+}
+float DynamicFontAtSize::get_descent() const {
+
+ return font->descent*scale;
+}
+
+Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next) const {
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(p_char);
+
+ const Character *c = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(!c,Size2());
+
+ Size2 ret( c->advance, get_height());
+
+ if (p_next) {
+ DynamicFontData::KerningPairKey kpk;
+ kpk.A=p_char;
+ kpk.B=p_next;
+
+ const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk);
+ if (K) {
+ ret.x+=K->get()*scale;
+ }
+
+ }
+
+ return ret;
+}
+
+
+float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+
+ const_cast<DynamicFontAtSize*>(this)->_update_char(p_char);
+
+ const Character * c = char_map.getptr(p_char);
+
+ if (!c) {
+ return 0;
+ }
+
+ Point2 cpos=p_pos;
+ cpos.x+=c->h_align;
+ cpos.y-=get_ascent();
+ cpos.y+=c->v_align;
+ ERR_FAIL_COND_V( c->texture_idx<-1 || c->texture_idx>=textures.size(),0);
+ if (c->texture_idx!=-1)
+ VisualServer::get_singleton()->canvas_item_add_texture_rect_region( p_canvas_item, Rect2( cpos, c->rect.size ), textures[c->texture_idx].texture->get_rid(),c->rect, p_modulate );
+
+ //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
+
+ float ret = c->advance;
+ if (p_next) {
+ DynamicFontData::KerningPairKey kpk;
+ kpk.A=p_char;
+ kpk.B=p_next;
+
+ const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk);
+ if (K) {
+ ret+=K->get()*scale;
+ }
+
+ }
+
+ return ret;
+}
+
+
+void DynamicFontAtSize::_update_char(CharType p_char) {
+
+ if (char_map.has(p_char))
+ return;
+
+ font->lock();
+
+
+ int w,h,xofs,yofs;
+ unsigned char * cpbitmap = stbtt_GetCodepointBitmap(&font->info, scale, scale, p_char, &w, &h, &xofs, &yofs );
+
+ if (!cpbitmap) {
+ //no glyph
+
+ int advance;
+ stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);
+ //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale));
+ Character ch;
+ ch.texture_idx=-1;
+ ch.advance=advance*scale;
+ ch.h_align=0;
+ ch.v_align=0;
+
+ char_map[p_char]=ch;
+
+ font->unlock();
+
+ return;
+ }
+
+ int mw=w+rect_margin*2;
+ int mh=h+rect_margin*2;
+
+ if (mw>4096 || mh>4096) {
+
+ stbtt_FreeBitmap(cpbitmap,NULL);
+ font->unlock();
+ ERR_FAIL_COND(mw>4096);
+ ERR_FAIL_COND(mh>4096);
+ }
+
+ //find a texture to fit this...
+
+ int tex_index=-1;
+ int tex_x=0;
+ int tex_y=0;
+
+ for(int i=0;i<textures.size();i++) {
+
+ CharTexture &ct=textures[i];
+
+ if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture
+ continue;
+
+ tex_y=0x7FFFFFFF;
+ tex_x=0;
+
+ for(int j=0;j<ct.texture_size-mw;j++) {
+
+ int max_y=0;
+
+ for(int k=j;k<j+mw;k++) {
+
+ int y = ct.offsets[k];
+ if (y>max_y)
+ max_y=y;
+ }
+
+ if (max_y<tex_y) {
+ tex_y=max_y;
+ tex_x=j;
+ }
+ }
+
+ if (tex_y==0x7FFFFFFF || tex_y+mh > ct.texture_size)
+ continue; //fail, could not fit it here
+
+ tex_index=i;
+ break;
+ }
+
+// print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y));
+
+ if (tex_index==-1) {
+ //could not find texture to fit, create one
+ tex_x = 0;
+ tex_y = 0;
+
+ int texsize = MAX(size*8,256);
+ if (mw>texsize)
+ texsize=mw; //special case, adapt to it?
+ if (mh>texsize)
+ texsize=mh; //special case, adapt to it?
+
+ texsize=nearest_power_of_2(texsize);
+
+ texsize=MIN(texsize,4096);
+
+
+ CharTexture tex;
+ tex.texture_size=texsize;
+ tex.imgdata.resize(texsize*texsize*2); //grayscale alpha
+
+ {
+ //zero texture
+ DVector<uint8_t>::Write w = tex.imgdata.write();
+ ERR_FAIL_COND(texsize*texsize*2 > tex.imgdata.size());
+ for(int i=0;i<texsize*texsize*2;i++) {
+ w[i]=0;
+ }
+ }
+ tex.offsets.resize(texsize);
+ for(int i=0;i<texsize;i++) //zero offsets
+ tex.offsets[i]=0;
+
+ textures.push_back(tex);
+ tex_index=textures.size()-1;
+
+ }
+
+
+ //fit character in char texture
+
+ CharTexture &tex=textures[tex_index];
+
+ {
+ DVector<uint8_t>::Write wr = tex.imgdata.write();
+
+ for(int i=0;i<h;i++) {
+ for(int j=0;j<w;j++) {
+
+ int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2;
+ ERR_FAIL_COND(ofs >= tex.imgdata.size());
+ wr[ofs+0]=255; //grayscale as 1
+ wr[ofs+1]=cpbitmap[i*w+j]; //alpha as 0
+ }
+ }
+ }
+
+ //blit to image and texture
+ {
+
+ Image img(tex.texture_size,tex.texture_size,0,Image::FORMAT_GRAYSCALE_ALPHA,tex.imgdata);
+
+ if (tex.texture.is_null()) {
+ tex.texture.instance();
+ tex.texture->create_from_image(img,Texture::FLAG_FILTER);
+ } else {
+ tex.texture->set_data(img); //update
+ }
+
+ }
+
+
+ // update height array
+
+ for(int k=tex_x;k<tex_x+mw;k++) {
+
+ tex.offsets[k]=tex_y+mh;
+ }
+
+ int advance;
+ stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0);
+
+ Character chr;
+ chr.h_align=xofs;
+ chr.v_align=yofs + get_ascent();
+ chr.advance=advance*scale;
+ chr.texture_idx=tex_index;
+
+
+ chr.rect=Rect2(tex_x+rect_margin,tex_y+rect_margin,w,h);
+
+ //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs));
+
+ char_map[p_char]=chr;
+
+ stbtt_FreeBitmap(cpbitmap,NULL);
+
+ font->unlock();
+
+}
+
+DynamicFontAtSize::DynamicFontAtSize() {
+
+ rect_margin=1;
+}
+
+DynamicFontAtSize::~DynamicFontAtSize(){
+
+ ERR_FAIL_COND(!font.ptr());
+ font->size_cache.erase(size);
+}
+
+/////////////////////////
+
+
+void DynamicFont::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_font_data","data:DynamicFontData"),&DynamicFont::set_font_data);
+ ObjectTypeDB::bind_method(_MD("get_font_data:DynamicFontData"),&DynamicFont::get_font_data);
+
+ ObjectTypeDB::bind_method(_MD("set_size","data"),&DynamicFont::set_size);
+ ObjectTypeDB::bind_method(_MD("get_size"),&DynamicFont::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"font/size"),_SCS("set_size"),_SCS("get_size"));
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"font/font",PROPERTY_HINT_RESOURCE_TYPE,"DynamicFontData"),_SCS("set_font_data"),_SCS("get_font_data"));
+}
+
+
+void DynamicFont::set_font_data(const Ref<DynamicFontData>& p_data) {
+
+ data=p_data;
+ data_at_size=data->_get_dynamic_font_at_size(size);
+}
+
+Ref<DynamicFontData> DynamicFont::get_font_data() const{
+
+ return data;
+}
+
+void DynamicFont::set_size(int p_size){
+
+ if (size==p_size)
+ return;
+ size=p_size;
+ ERR_FAIL_COND(p_size<1);
+ if (!data.is_valid())
+ return;
+ data_at_size=data->_get_dynamic_font_at_size(size);
+
+}
+int DynamicFont::get_size() const{
+
+ return size;
+}
+
+float DynamicFont::get_height() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_height();
+}
+
+float DynamicFont::get_ascent() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_ascent();
+}
+
+float DynamicFont::get_descent() const{
+
+ if (!data_at_size.is_valid())
+ return 1;
+
+ return data_at_size->get_descent();
+
+}
+
+Size2 DynamicFont::get_char_size(CharType p_char,CharType p_next) const{
+
+ if (!data_at_size.is_valid())
+ return Size2(1,1);
+
+ return data_at_size->get_char_size(p_char,p_next);
+
+}
+
+bool DynamicFont::is_distance_field_hint() const{
+
+ return false;
+}
+
+float DynamicFont::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+
+ if (!data_at_size.is_valid())
+ return 0;
+
+ return data_at_size->draw_char(p_canvas_item,p_pos,p_char,p_next,p_modulate);
+
+}
+
+DynamicFont::DynamicFont() {
+
+ size=16;
+}
+
+DynamicFont::~DynamicFont() {
+
+}
+
+/////////////////////////
+
+
+RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_FILE_CANT_OPEN;
+
+
+ FileAccess *f = FileAccess::open(p_path,FileAccess::READ);
+ ERR_FAIL_COND_V(!f,RES());
+
+ DVector<uint8_t> data;
+
+ data.resize(f->get_len());
+
+ ERR_FAIL_COND_V(data.size()==0,RES());
+
+ {
+ DVector<uint8_t>::Write w = data.write();
+ f->get_buffer(w.ptr(),data.size());
+ }
+
+ Ref<DynamicFontData> dfd;
+ dfd.instance();
+ dfd->set_font_data(data);
+
+ if (r_error)
+ *r_error=OK;
+
+ return dfd;
+}
+
+void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ttf");
+}
+
+bool ResourceFormatLoaderDynamicFont::handles_type(const String& p_type) const {
+
+ return (p_type=="DynamicFontData");
+}
+
+String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const {
+
+ String el = p_path.extension().to_lower();
+ if (el=="ttf")
+ return "DynamicFontData";
+ return "";
+}
+
+#endif
diff --git a/scene/resources/dynamic_font_stb.h b/scene/resources/dynamic_font_stb.h
new file mode 100644
index 0000000000..6b72fb3703
--- /dev/null
+++ b/scene/resources/dynamic_font_stb.h
@@ -0,0 +1,178 @@
+#ifndef DYNAMICFONT_STB_H
+#define DYNAMICFONT_STB_H
+
+#ifndef FREETYPE_ENABLED
+
+#include "font.h"
+#include "stb_truetype.h"
+#include "io/resource_loader.h"
+
+
+
+class DynamicFontAtSize;
+class DynamicFont;
+
+class DynamicFontData : public Resource {
+
+ OBJ_TYPE(DynamicFontData,Resource);
+
+ bool valid;
+
+ DVector<uint8_t> font_data;
+ DVector<uint8_t>::Read fr;
+ const uint8_t* last_data_ptr;
+
+ struct KerningPairKey {
+
+ union {
+ struct {
+ uint32_t A,B;
+ };
+
+ uint64_t pair;
+ };
+
+ _FORCE_INLINE_ bool operator<(const KerningPairKey& p_r) const { return pair<p_r.pair; }
+ };
+
+ Map<KerningPairKey,int> kerning_map;
+
+
+ Map<int,DynamicFontAtSize*> size_cache;
+
+friend class DynamicFontAtSize;
+
+ stbtt_fontinfo info;
+ int ascent;
+ int descent;
+ int linegap;
+
+ void lock();
+ void unlock();
+
+friend class DynamicFont;
+
+
+ Ref<DynamicFontAtSize> _get_dynamic_font_at_size(int p_size);
+public:
+
+ void set_font_data(const DVector<uint8_t>& p_font);
+ DynamicFontData();
+ ~DynamicFontData();
+};
+
+
+class DynamicFontAtSize : public Reference {
+
+ OBJ_TYPE(DynamicFontAtSize,Reference);
+
+
+ int rect_margin;
+
+ struct CharTexture {
+
+ DVector<uint8_t> imgdata;
+ int texture_size;
+ Vector<int> offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ Vector<CharTexture> textures;
+
+ struct Character {
+
+ int texture_idx;
+ Rect2 rect;
+ float v_align;
+ float h_align;
+ float advance;
+
+ Character() { texture_idx=0; v_align=0; }
+ };
+
+
+
+ HashMap< CharType, Character > char_map;
+
+ _FORCE_INLINE_ void _update_char(CharType p_char);
+
+friend class DynamicFontData;
+ Ref<DynamicFontData> font;
+ float scale;
+ int size;
+
+protected:
+
+public:
+
+ float get_height() const;
+
+ float get_ascent() const;
+ float get_descent() const;
+
+ Size2 get_char_size(CharType p_char,CharType p_next=0) const;
+
+ float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
+
+
+
+ DynamicFontAtSize();
+ ~DynamicFontAtSize();
+};
+
+///////////////
+
+class DynamicFont : public Font {
+
+ OBJ_TYPE( DynamicFont, Font );
+
+ Ref<DynamicFontData> data;
+ Ref<DynamicFontAtSize> data_at_size;
+ int size;
+
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void set_font_data(const Ref<DynamicFontData>& p_data);
+ Ref<DynamicFontData> get_font_data() const;
+
+ void set_size(int p_size);
+ int get_size() const;
+
+ virtual float get_height() const;
+
+ virtual float get_ascent() const;
+ virtual float get_descent() const;
+
+ virtual Size2 get_char_size(CharType p_char,CharType p_next=0) const;
+
+ virtual bool is_distance_field_hint() const;
+
+ virtual float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
+
+ DynamicFont();
+ ~DynamicFont();
+
+};
+
+
+
+/////////////
+
+class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader {
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+
+#endif
+#endif // DYNAMICFONT_H
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index ef5b7a3eb0..0d8d224037 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -31,7 +31,67 @@
#include "core/os/file_access.h"
#include "core/io/resource_loader.h"
-void Font::_set_chars(const DVector<int>& p_chars) {
+
+
+void Font::draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate) const {
+
+ float length=get_string_size(p_text).width;
+ if (length>=p_width) {
+ draw(p_canvas_item,p_pos,p_text,p_modulate,p_width);
+ return;
+ }
+
+ float ofs;
+ switch(p_align) {
+ case HALIGN_LEFT: {
+ ofs=0;
+ } break;
+ case HALIGN_CENTER: {
+ ofs = Math::floor( (p_width-length) / 2.0 );
+ } break;
+ case HALIGN_RIGHT: {
+ ofs=p_width-length;
+ } break;
+ }
+ draw(p_canvas_item,p_pos+Point2(ofs,0),p_text,p_modulate,p_width);
+}
+
+void Font::draw(RID p_canvas_item, const Point2& p_pos, const String& p_text, const Color& p_modulate,int p_clip_w) const {
+
+ Vector2 ofs;
+
+ for (int i=0;i<p_text.length();i++) {
+
+ int width = get_char_size(p_text[i]).width;
+
+ if (p_clip_w>=0 && (ofs.x+width)>p_clip_w)
+ break; //clip
+
+ ofs.x+=draw_char(p_canvas_item,p_pos+ofs,p_text[i],p_text[i+1],p_modulate);
+ }
+}
+
+void Font::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("draw","canvas_item","pos","string","modulate","clip_w"),&Font::draw,DEFVAL(Color(1,1,1)),DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("get_ascent"),&Font::get_ascent);
+ ObjectTypeDB::bind_method(_MD("get_descent"),&Font::get_descent);
+ ObjectTypeDB::bind_method(_MD("get_height"),&Font::get_height);
+ ObjectTypeDB::bind_method(_MD("is_distance_field_hint"),&Font::is_distance_field_hint);
+ ObjectTypeDB::bind_method(_MD("get_string_size","string"),&Font::get_string_size);
+ ObjectTypeDB::bind_method(_MD("draw_char","canvas_item","pos","char","next","modulate"),&Font::draw_char,DEFVAL(-1),DEFVAL(Color(1,1,1)));
+
+}
+
+
+Font::Font() {
+
+
+}
+
+/////////////////////////////////////////////////////////////////
+
+void BitmapFont::_set_chars(const DVector<int>& p_chars) {
int len = p_chars.size();
//char 1 charsize 1 texture, 4 rect, 2 align, advance 1
@@ -50,7 +110,7 @@ void Font::_set_chars(const DVector<int>& p_chars) {
}
-DVector<int> Font::_get_chars() const {
+DVector<int> BitmapFont::_get_chars() const {
DVector<int> chars;
@@ -74,7 +134,7 @@ DVector<int> Font::_get_chars() const {
return chars;
}
-void Font::_set_kernings(const DVector<int>& p_kernings) {
+void BitmapFont::_set_kernings(const DVector<int>& p_kernings) {
int len=p_kernings.size();
ERR_FAIL_COND(len%3);
@@ -89,7 +149,7 @@ void Font::_set_kernings(const DVector<int>& p_kernings) {
}
}
-DVector<int> Font::_get_kernings() const {
+DVector<int> BitmapFont::_get_kernings() const {
DVector<int> kernings;
@@ -104,7 +164,7 @@ DVector<int> Font::_get_kernings() const {
}
-void Font::_set_textures(const Vector<Variant> & p_textures) {
+void BitmapFont::_set_textures(const Vector<Variant> & p_textures) {
for(int i=0;i<p_textures.size();i++) {
Ref<Texture> tex = p_textures[i];
@@ -114,7 +174,7 @@ void Font::_set_textures(const Vector<Variant> & p_textures) {
}
-Vector<Variant> Font::_get_textures() const {
+Vector<Variant> BitmapFont::_get_textures() const {
Vector<Variant> rtextures;
for(int i=0;i<textures.size();i++)
@@ -122,7 +182,7 @@ Vector<Variant> Font::_get_textures() const {
return rtextures;
}
-Error Font::create_from_fnt(const String& p_string) {
+Error BitmapFont::create_from_fnt(const String& p_string) {
//fnt format used by angelcode bmfont
//http://www.angelcode.com/products/bmfont/
@@ -271,51 +331,51 @@ Error Font::create_from_fnt(const String& p_string) {
-void Font::set_height(float p_height) {
+void BitmapFont::set_height(float p_height) {
height=p_height;
}
-float Font::get_height() const{
+float BitmapFont::get_height() const{
return height;
}
-void Font::set_ascent(float p_ascent){
+void BitmapFont::set_ascent(float p_ascent){
ascent=p_ascent;
}
-float Font::get_ascent() const {
+float BitmapFont::get_ascent() const {
return ascent;
}
-float Font::get_descent() const {
+float BitmapFont::get_descent() const {
return height-ascent;
}
-void Font::add_texture(const Ref<Texture>& p_texture) {
+void BitmapFont::add_texture(const Ref<Texture>& p_texture) {
ERR_FAIL_COND( p_texture.is_null());
textures.push_back( p_texture );
}
-int Font::get_texture_count() const {
+int BitmapFont::get_texture_count() const {
return textures.size();
};
-Ref<Texture> Font::get_texture(int p_idx) const {
+Ref<Texture> BitmapFont::get_texture(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, textures.size(), Ref<Texture>());
return textures[p_idx];
};
-int Font::get_character_count() const {
+int BitmapFont::get_character_count() const {
return char_map.size();
};
-Vector<CharType> Font::get_char_keys() const {
+Vector<CharType> BitmapFont::get_char_keys() const {
Vector<CharType> chars;
chars.resize(char_map.size());
@@ -329,7 +389,7 @@ Vector<CharType> Font::get_char_keys() const {
return chars;
};
-Font::Character Font::get_character(CharType p_char) const {
+BitmapFont::Character BitmapFont::get_character(CharType p_char) const {
if (!char_map.has(p_char)) {
ERR_FAIL_V(Character());
@@ -338,7 +398,7 @@ Font::Character Font::get_character(CharType p_char) const {
return char_map[p_char];
};
-void Font::add_char(CharType p_char, int p_texture_idx, const Rect2& p_rect, const Size2& p_align, float p_advance) {
+void BitmapFont::add_char(CharType p_char, int p_texture_idx, const Rect2& p_rect, const Size2& p_align, float p_advance) {
if (p_advance<0)
p_advance=p_rect.size.width;
@@ -353,7 +413,7 @@ void Font::add_char(CharType p_char, int p_texture_idx, const Rect2& p_rect, con
char_map[p_char]=c;
}
-void Font::add_kerning_pair(CharType p_A,CharType p_B,int p_kerning) {
+void BitmapFont::add_kerning_pair(CharType p_A,CharType p_B,int p_kerning) {
KerningPairKey kpk;
@@ -369,10 +429,10 @@ void Font::add_kerning_pair(CharType p_A,CharType p_B,int p_kerning) {
}
}
-Vector<Font::KerningPairKey> Font::get_kerning_pair_keys() const {
+Vector<BitmapFont::KerningPairKey> BitmapFont::get_kerning_pair_keys() const {
- Vector<Font::KerningPairKey> ret;
+ Vector<BitmapFont::KerningPairKey> ret;
ret.resize(kerning_map.size());
int i=0;
@@ -385,7 +445,7 @@ Vector<Font::KerningPairKey> Font::get_kerning_pair_keys() const {
}
-int Font::get_kerning_pair(CharType p_A,CharType p_B) const {
+int BitmapFont::get_kerning_pair(CharType p_A,CharType p_B) const {
KerningPairKey kpk;
kpk.A=p_A;
@@ -398,19 +458,19 @@ int Font::get_kerning_pair(CharType p_A,CharType p_B) const {
return 0;
}
-void Font::set_distance_field_hint(bool p_distance_field) {
+void BitmapFont::set_distance_field_hint(bool p_distance_field) {
distance_field_hint=p_distance_field;
emit_changed();
}
-bool Font::is_distance_field_hint() const{
+bool BitmapFont::is_distance_field_hint() const{
return distance_field_hint;
}
-void Font::clear() {
+void BitmapFont::clear() {
height=1;
ascent=0;
@@ -426,7 +486,7 @@ Size2 Font::get_string_size(const String& p_string) const {
int l = p_string.length();
if (l==0)
- return Size2(0,height);
+ return Size2(0,get_height());
const CharType *sptr = &p_string[0];
for (int i=0;i<l;i++) {
@@ -434,48 +494,19 @@ Size2 Font::get_string_size(const String& p_string) const {
w+=get_char_size(sptr[i],sptr[i+1]).width;
}
- return Size2(w,height);
+ return Size2(w,get_height());
}
+void BitmapFont::set_fallback(const Ref<BitmapFont> &p_fallback) {
-void Font::draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate) const {
-
- float length=get_string_size(p_text).width;
- if (length>=p_width) {
- draw(p_canvas_item,p_pos,p_text,p_modulate,p_width);
- return;
- }
-
- float ofs;
- switch(p_align) {
- case HALIGN_LEFT: {
- ofs=0;
- } break;
- case HALIGN_CENTER: {
- ofs = Math::floor( (p_width-length) / 2.0 );
- } break;
- case HALIGN_RIGHT: {
- ofs=p_width-length;
- } break;
- }
- draw(p_canvas_item,p_pos+Point2(ofs,0),p_text,p_modulate,p_width);
+ fallback=p_fallback;
}
-void Font::draw(RID p_canvas_item, const Point2& p_pos, const String& p_text, const Color& p_modulate,int p_clip_w) const {
+Ref<BitmapFont> BitmapFont::get_fallback() const{
- Vector2 ofs;
-
- for (int i=0;i<p_text.length();i++) {
-
- int width = get_char_size(p_text[i]).width;
-
- if (p_clip_w>=0 && (ofs.x+width)>p_clip_w)
- break; //clip
-
- ofs.x+=draw_char(p_canvas_item,p_pos+ofs,p_text[i],p_text[i+1],p_modulate);
- }
+ return fallback;
}
-float Font::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
+float BitmapFont::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const {
const Character * c = char_map.getptr(p_char);
@@ -496,58 +527,70 @@ float Font::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_
return get_char_size(p_char,p_next).width;
}
-void Font::set_fallback(const Ref<Font> &p_fallback) {
- fallback=p_fallback;
-}
+Size2 BitmapFont::get_char_size(CharType p_char,CharType p_next) const {
-Ref<Font> Font::get_fallback() const{
+ const Character * c = char_map.getptr(p_char);
- return fallback;
+ if (!c) {
+ if (fallback.is_valid())
+ return fallback->get_char_size(p_char,p_next);
+ return Size2();
+ }
+
+ Size2 ret(c->advance,c->rect.size.y);
+
+ if (p_next) {
+
+ KerningPairKey kpk;
+ kpk.A=p_char;
+ kpk.B=p_next;
+
+ const Map<KerningPairKey,int>::Element *E=kerning_map.find(kpk);
+ if (E) {
+
+ ret.width-=E->get();
+ }
+ }
+
+ return ret;
}
-void Font::_bind_methods() {
+void BitmapFont::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("create_from_fnt","path"),&Font::create_from_fnt);
- ObjectTypeDB::bind_method(_MD("set_height","px"),&Font::set_height);
- ObjectTypeDB::bind_method(_MD("get_height"),&Font::get_height);
+ ObjectTypeDB::bind_method(_MD("create_from_fnt","path"),&BitmapFont::create_from_fnt);
+ ObjectTypeDB::bind_method(_MD("set_height","px"),&BitmapFont::set_height);
- ObjectTypeDB::bind_method(_MD("set_ascent","px"),&Font::set_ascent);
- ObjectTypeDB::bind_method(_MD("get_ascent"),&Font::get_ascent);
- ObjectTypeDB::bind_method(_MD("get_descent"),&Font::get_descent);
+ ObjectTypeDB::bind_method(_MD("set_ascent","px"),&BitmapFont::set_ascent);
- ObjectTypeDB::bind_method(_MD("add_kerning_pair","char_a","char_b","kerning"),&Font::add_kerning_pair);
- ObjectTypeDB::bind_method(_MD("get_kerning_pair","char_a","char_b"),&Font::get_kerning_pair);
+ ObjectTypeDB::bind_method(_MD("add_kerning_pair","char_a","char_b","kerning"),&BitmapFont::add_kerning_pair);
+ ObjectTypeDB::bind_method(_MD("get_kerning_pair","char_a","char_b"),&BitmapFont::get_kerning_pair);
- ObjectTypeDB::bind_method(_MD("add_texture","texture:Texture"),&Font::add_texture);
- ObjectTypeDB::bind_method(_MD("add_char","character","texture","rect","align","advance"),&Font::add_char,DEFVAL(Point2()),DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("add_texture","texture:Texture"),&BitmapFont::add_texture);
+ ObjectTypeDB::bind_method(_MD("add_char","character","texture","rect","align","advance"),&BitmapFont::add_char,DEFVAL(Point2()),DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("get_texture_count"),&Font::get_texture_count);
- ObjectTypeDB::bind_method(_MD("get_texture:Texture","idx"),&Font::get_texture);
+ ObjectTypeDB::bind_method(_MD("get_texture_count"),&BitmapFont::get_texture_count);
+ ObjectTypeDB::bind_method(_MD("get_texture:Texture","idx"),&BitmapFont::get_texture);
- ObjectTypeDB::bind_method(_MD("get_char_size","char","next"),&Font::get_char_size,DEFVAL(0));
- ObjectTypeDB::bind_method(_MD("get_string_size","string"),&Font::get_string_size);
+ ObjectTypeDB::bind_method(_MD("get_char_size","char","next"),&BitmapFont::get_char_size,DEFVAL(0));
- ObjectTypeDB::bind_method(_MD("set_distance_field_hint","enable"),&Font::set_distance_field_hint);
- ObjectTypeDB::bind_method(_MD("is_distance_field_hint"),&Font::is_distance_field_hint);
+ ObjectTypeDB::bind_method(_MD("set_distance_field_hint","enable"),&BitmapFont::set_distance_field_hint);
- ObjectTypeDB::bind_method(_MD("clear"),&Font::clear);
+ ObjectTypeDB::bind_method(_MD("clear"),&BitmapFont::clear);
- ObjectTypeDB::bind_method(_MD("draw","canvas_item","pos","string","modulate","clip_w"),&Font::draw,DEFVAL(Color(1,1,1)),DEFVAL(-1));
- ObjectTypeDB::bind_method(_MD("draw_char","canvas_item","pos","char","next","modulate"),&Font::draw_char,DEFVAL(-1),DEFVAL(Color(1,1,1)));
- ObjectTypeDB::bind_method(_MD("_set_chars"),&Font::_set_chars);
- ObjectTypeDB::bind_method(_MD("_get_chars"),&Font::_get_chars);
+ ObjectTypeDB::bind_method(_MD("_set_chars"),&BitmapFont::_set_chars);
+ ObjectTypeDB::bind_method(_MD("_get_chars"),&BitmapFont::_get_chars);
- ObjectTypeDB::bind_method(_MD("_set_kernings"),&Font::_set_kernings);
- ObjectTypeDB::bind_method(_MD("_get_kernings"),&Font::_get_kernings);
+ ObjectTypeDB::bind_method(_MD("_set_kernings"),&BitmapFont::_set_kernings);
+ ObjectTypeDB::bind_method(_MD("_get_kernings"),&BitmapFont::_get_kernings);
- ObjectTypeDB::bind_method(_MD("_set_textures"),&Font::_set_textures);
- ObjectTypeDB::bind_method(_MD("_get_textures"),&Font::_get_textures);
+ ObjectTypeDB::bind_method(_MD("_set_textures"),&BitmapFont::_set_textures);
+ ObjectTypeDB::bind_method(_MD("_get_textures"),&BitmapFont::_get_textures);
- ObjectTypeDB::bind_method(_MD("set_fallback","fallback"),&Font::set_fallback);
- ObjectTypeDB::bind_method(_MD("get_fallback"),&Font::get_fallback);
+ ObjectTypeDB::bind_method(_MD("set_fallback","fallback"),&BitmapFont::set_fallback);
+ ObjectTypeDB::bind_method(_MD("get_fallback"),&BitmapFont::get_fallback);
ADD_PROPERTY( PropertyInfo( Variant::ARRAY, "textures", PROPERTY_HINT_NONE,"", PROPERTY_USAGE_NOEDITOR ), _SCS("_set_textures"), _SCS("_get_textures") );
ADD_PROPERTY( PropertyInfo( Variant::INT_ARRAY, "chars", PROPERTY_HINT_NONE,"", PROPERTY_USAGE_NOEDITOR ), _SCS("_set_chars"), _SCS("_get_chars") );
@@ -556,11 +599,11 @@ void Font::_bind_methods() {
ADD_PROPERTY( PropertyInfo( Variant::REAL, "height", PROPERTY_HINT_RANGE,"-1024,1024,1" ), _SCS("set_height"), _SCS("get_height") );
ADD_PROPERTY( PropertyInfo( Variant::REAL, "ascent", PROPERTY_HINT_RANGE,"-1024,1024,1" ), _SCS("set_ascent"), _SCS("get_ascent") );
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "distance_field" ), _SCS("set_distance_field_hint"), _SCS("is_distance_field_hint") );
- ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE,"Font" ), _SCS("set_fallback"), _SCS("get_fallback") );
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "fallback", PROPERTY_HINT_RESOURCE_TYPE,"BitmapFont" ), _SCS("set_fallback"), _SCS("get_fallback") );
}
-Font::Font() {
+BitmapFont::BitmapFont() {
clear();
@@ -569,7 +612,7 @@ Font::Font() {
}
-Font::~Font() {
+BitmapFont::~BitmapFont() {
clear();
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 03b1ec5191..91f4874932 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -35,9 +35,40 @@
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
+
+
class Font : public Resource {
OBJ_TYPE( Font, Resource );
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ virtual float get_height() const=0;
+
+ virtual float get_ascent() const=0;
+ virtual float get_descent() const=0;
+
+ virtual Size2 get_char_size(CharType p_char,CharType p_next=0) const=0;
+ Size2 get_string_size(const String& p_string) const;
+
+ virtual bool is_distance_field_hint() const=0;
+
+ void draw(RID p_canvas_item, const Point2& p_pos, const String& p_text,const Color& p_modulate=Color(1,1,1),int p_clip_w=-1) const;
+ void draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate=Color(1,1,1)) const;
+ virtual float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const=0;
+
+ Font();
+
+};
+
+
+class BitmapFont : public Font {
+
+ OBJ_TYPE( BitmapFont, Font );
RES_BASE_EXTENSION("fnt");
Vector< Ref<Texture> > textures;
@@ -84,7 +115,7 @@ private:
void _set_textures(const Vector<Variant> & p_textures);
Vector<Variant> _get_textures() const;
- Ref<Font> fallback;
+ Ref<BitmapFont> fallback;
protected:
static void _bind_methods();
@@ -114,54 +145,23 @@ public:
int get_kerning_pair(CharType p_A,CharType p_B) const;
Vector<KerningPairKey> get_kerning_pair_keys() const;
- inline Size2 get_char_size(CharType p_char,CharType p_next=0) const;
- Size2 get_string_size(const String& p_string) const;
-
+ Size2 get_char_size(CharType p_char,CharType p_next=0) const;
- void set_fallback(const Ref<Font> &p_fallback);
- Ref<Font> get_fallback() const;
+ void set_fallback(const Ref<BitmapFont> &p_fallback);
+ Ref<BitmapFont> get_fallback() const;
void clear();
void set_distance_field_hint(bool p_distance_field);
bool is_distance_field_hint() const;
- void draw(RID p_canvas_item, const Point2& p_pos, const String& p_text,const Color& p_modulate=Color(1,1,1),int p_clip_w=-1) const;
- void draw_halign(RID p_canvas_item, const Point2& p_pos, HAlign p_align,float p_width,const String& p_text,const Color& p_modulate=Color(1,1,1)) const;
float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const;
- Font();
- ~Font();
+ BitmapFont();
+ ~BitmapFont();
};
-Size2 Font::get_char_size(CharType p_char,CharType p_next) const {
-
- const Character * c = char_map.getptr(p_char);
-
- if (!c) {
- if (fallback.is_valid())
- return fallback->get_char_size(p_char,p_next);
- return Size2();
- }
-
- Size2 ret(c->advance,c->rect.size.y);
-
- if (p_next) {
-
- KerningPairKey kpk;
- kpk.A=p_char;
- kpk.B=p_next;
-
- const Map<KerningPairKey,int>::Element *E=kerning_map.find(kpk);
- if (E) {
-
- ret.width-=E->get();
- }
- }
-
- return ret;
-}
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 09c0a21f53..e6356d3366 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -30,7 +30,6 @@
#include "scene/resources/concave_polygon_shape.h"
#include "scene/resources/convex_polygon_shape.h"
#include "surface_tool.h"
-
static const char*_array_name[]={
"vertex_array",
"normal_array",
@@ -288,6 +287,7 @@ void Mesh::add_surface(PrimitiveType p_primitive,const Array& p_arrays,const Arr
triangle_mesh=Ref<TriangleMesh>();
_change_notify();
+ emit_changed();
}
@@ -387,6 +387,7 @@ void Mesh::surface_remove(int p_idx) {
triangle_mesh=Ref<TriangleMesh>();
_recompute_aabb();
_change_notify();
+ emit_changed();
}
@@ -491,6 +492,8 @@ void Mesh::add_surface_from_mesh_data(const Geometry::MeshData& p_mesh_data) {
surfaces.push_back(s);
_change_notify();
+
+ emit_changed();
}
RID Mesh::get_rid() const {
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index 76bb1c585f..2b1d022299 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -48,6 +48,8 @@ bool MeshLibrary::_set(const StringName& p_name, const Variant& p_value) {
set_item_shape(idx,p_value);
else if(what=="preview")
set_item_preview(idx,p_value);
+ else if(what=="navmesh")
+ set_item_navmesh(idx,p_value);
else
return false;
@@ -70,6 +72,8 @@ bool MeshLibrary::_get(const StringName& p_name,Variant &r_ret) const {
r_ret= get_item_mesh(idx);
else if(what=="shape")
r_ret= get_item_shape(idx);
+ else if(what=="navmesh")
+ r_ret= get_item_navmesh(idx);
else if(what=="preview")
r_ret= get_item_preview(idx);
else
@@ -86,6 +90,7 @@ void MeshLibrary::_get_property_list( List<PropertyInfo> *p_list) const {
p_list->push_back( PropertyInfo(Variant::STRING,name+"name"));
p_list->push_back( PropertyInfo(Variant::OBJECT,name+"mesh",PROPERTY_HINT_RESOURCE_TYPE,"Mesh"));
p_list->push_back( PropertyInfo(Variant::OBJECT,name+"shape",PROPERTY_HINT_RESOURCE_TYPE,"Shape"));
+ p_list->push_back( PropertyInfo(Variant::OBJECT,name+"navmesh",PROPERTY_HINT_RESOURCE_TYPE,"NavigationMesh"));
p_list->push_back( PropertyInfo(Variant::OBJECT,name+"preview",PROPERTY_HINT_RESOURCE_TYPE,"Texture",PROPERTY_USAGE_DEFAULT|PROPERTY_USAGE_EDITOR_HELPER));
}
}
@@ -130,6 +135,18 @@ void MeshLibrary::set_item_shape(int p_item,const Ref<Shape>& p_shape) {
}
+
+void MeshLibrary::set_item_navmesh(int p_item,const Ref<NavigationMesh>& p_navmesh) {
+
+ ERR_FAIL_COND(!item_map.has(p_item));
+ item_map[p_item].navmesh=p_navmesh;
+ _change_notify();
+ notify_change_to_owners();
+ emit_changed();
+ _change_notify();
+
+}
+
void MeshLibrary::set_item_preview(int p_item,const Ref<Texture>& p_preview) {
ERR_FAIL_COND(!item_map.has(p_item));
@@ -157,6 +174,13 @@ Ref<Shape> MeshLibrary::get_item_shape(int p_item) const {
return item_map[p_item].shape;
}
+Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const {
+
+ ERR_FAIL_COND_V(!item_map.has(p_item),Ref<NavigationMesh>());
+ return item_map[p_item].navmesh;
+}
+
+
Ref<Texture> MeshLibrary::get_item_preview(int p_item) const {
ERR_FAIL_COND_V(!item_map.has(p_item),Ref<Texture>());
@@ -223,9 +247,11 @@ void MeshLibrary::_bind_methods() {
ObjectTypeDB::bind_method(_MD("create_item","id"),&MeshLibrary::create_item);
ObjectTypeDB::bind_method(_MD("set_item_name","id","name"),&MeshLibrary::set_item_name);
ObjectTypeDB::bind_method(_MD("set_item_mesh","id","mesh:Mesh"),&MeshLibrary::set_item_mesh);
+ ObjectTypeDB::bind_method(_MD("set_item_navmesh","id","navmesh:NavigationMesh"),&MeshLibrary::set_item_navmesh);
ObjectTypeDB::bind_method(_MD("set_item_shape","id","shape:Shape"),&MeshLibrary::set_item_shape);
ObjectTypeDB::bind_method(_MD("get_item_name","id"),&MeshLibrary::get_item_name);
ObjectTypeDB::bind_method(_MD("get_item_mesh:Mesh","id"),&MeshLibrary::get_item_mesh);
+ ObjectTypeDB::bind_method(_MD("get_item_navmesh:NavigationMesh","id"),&MeshLibrary::get_item_navmesh);
ObjectTypeDB::bind_method(_MD("get_item_shape:Shape","id"),&MeshLibrary::get_item_shape);
ObjectTypeDB::bind_method(_MD("remove_item","id"),&MeshLibrary::remove_item);
ObjectTypeDB::bind_method(_MD("clear"),&MeshLibrary::clear);
diff --git a/scene/resources/mesh_library.h b/scene/resources/mesh_library.h
index bf0107d7a9..e4dba193fc 100644
--- a/scene/resources/mesh_library.h
+++ b/scene/resources/mesh_library.h
@@ -33,6 +33,7 @@
#include "mesh.h"
#include "shape.h"
#include "map.h"
+#include "scene/3d/navigation_mesh.h"
class MeshLibrary : public Resource {
@@ -40,11 +41,11 @@ class MeshLibrary : public Resource {
RES_BASE_EXTENSION("gt");
struct Item {
-
String name;
Ref<Mesh> mesh;
Ref<Shape> shape;
Ref<Texture> preview;
+ Ref<NavigationMesh> navmesh;
};
Map<int,Item> item_map;
@@ -62,10 +63,12 @@ public:
void create_item(int p_item);
void set_item_name(int p_item,const String& p_name);
void set_item_mesh(int p_item,const Ref<Mesh>& p_mesh);
+ void set_item_navmesh(int p_item, const Ref<NavigationMesh>& p_navmesh);
void set_item_shape(int p_item,const Ref<Shape>& p_shape);
void set_item_preview(int p_item,const Ref<Texture>& p_preview);
String get_item_name(int p_item) const;
Ref<Mesh> get_item_mesh(int p_item) const;
+ Ref<NavigationMesh> get_item_navmesh(int p_item) const;
Ref<Shape> get_item_shape(int p_item) const;
Ref<Texture> get_item_preview(int p_item) const;
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index ef04c68e8e..5df22ba8cc 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -1493,7 +1493,7 @@ int SceneState::get_connection_flags(int p_idx) const{
Array SceneState::get_connection_binds(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx,connections.size(),-1);
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),Array());
Array binds;
for(int i=0;i<connections[p_idx].binds.size();i++) {
binds.push_back(variants[connections[p_idx].binds[i]]);
diff --git a/scene/resources/stb_truetype.h b/scene/resources/stb_truetype.h
new file mode 100644
index 0000000000..016972785a
--- /dev/null
+++ b/scene/resources/stb_truetype.h
@@ -0,0 +1,3267 @@
+// stb_truetype.h - v1.11 - public domain
+// authored from 2009-2015 by Sean Barrett / RAD Game Tools
+//
+// This library processes TrueType files:
+// parse files
+// extract glyph metrics
+// extract glyph shapes
+// render glyphs to one-channel bitmaps with antialiasing (box filter)
+//
+// Todo:
+// non-MS cmaps
+// crashproof on bad data
+// hinting? (no longer patented)
+// cleartype-style AA?
+// optimize: use simple memory allocator for intermediates
+// optimize: build edge-list directly from curves
+// optimize: rasterize directly from curves?
+//
+// ADDITIONAL CONTRIBUTORS
+//
+// Mikko Mononen: compound shape support, more cmap formats
+// Tor Andersson: kerning, subpixel rendering
+//
+// Misc other:
+// Ryan Gordon
+// Simon Glass
+//
+// Bug/warning reports/fixes:
+// "Zer" on mollyrocket (with fix)
+// Cass Everitt
+// stoiko (Haemimont Games)
+// Brian Hook
+// Walter van Niftrik
+// David Gow
+// David Given
+// Ivan-Assen Ivanov
+// Anthony Pesch
+// Johan Duparc
+// Hou Qiming
+// Fabian "ryg" Giesen
+// Martins Mozeiko
+// Cap Petschulat
+// Omar Cornut
+// github:aloucks
+// Peter LaValle
+// Sergey Popov
+// Giumo X. Clanjor
+// Higor Euripedes
+// Thomas Fields
+// Derek Vinyard
+//
+// VERSION HISTORY
+//
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
+// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
+// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
+// variant PackFontRanges to pack and render in separate phases;
+// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
+// fixed an assert() bug in the new rasterizer
+// replace assert() with STBTT_assert() in new rasterizer
+// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
+// also more precise AA rasterizer, except if shapes overlap
+// remove need for STBTT_sort
+// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
+// 1.04 (2015-04-15) typo in example
+// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
+//
+// Full history can be found at the end of this file.
+//
+// LICENSE
+//
+// This software is dual-licensed to the public domain and under the following
+// license: you are granted a perpetual, irrevocable license to copy, modify,
+// publish, and distribute this file as you see fit.
+//
+// USAGE
+//
+// Include this file in whatever places neeed to refer to it. In ONE C/C++
+// file, write:
+// #define STB_TRUETYPE_IMPLEMENTATION
+// before the #include of this file. This expands out the actual
+// implementation into that C/C++ file.
+//
+// To make the implementation private to the file that generates the implementation,
+// #define STBTT_STATIC
+//
+// Simple 3D API (don't ship this, but it's fine for tools and quick start)
+// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
+// stbtt_GetBakedQuad() -- compute quad to draw for a given char
+//
+// Improved 3D API (more shippable):
+// #include "stb_rect_pack.h" -- optional, but you really want it
+// stbtt_PackBegin()
+// stbtt_PackSetOversample() -- for improved quality on small fonts
+// stbtt_PackFontRanges() -- pack and renders
+// stbtt_PackEnd()
+// stbtt_GetPackedQuad()
+//
+// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
+// stbtt_InitFont()
+// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
+//
+// Render a unicode codepoint to a bitmap
+// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
+// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
+// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
+//
+// Character advance/positioning
+// stbtt_GetCodepointHMetrics()
+// stbtt_GetFontVMetrics()
+// stbtt_GetCodepointKernAdvance()
+//
+// Starting with version 1.06, the rasterizer was replaced with a new,
+// faster and generally-more-precise rasterizer. The new rasterizer more
+// accurately measures pixel coverage for anti-aliasing, except in the case
+// where multiple shapes overlap, in which case it overestimates the AA pixel
+// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
+// this turns out to be a problem, you can re-enable the old rasterizer with
+// #define STBTT_RASTERIZER_VERSION 1
+// which will incur about a 15% speed hit.
+//
+// ADDITIONAL DOCUMENTATION
+//
+// Immediately after this block comment are a series of sample programs.
+//
+// After the sample programs is the "header file" section. This section
+// includes documentation for each API function.
+//
+// Some important concepts to understand to use this library:
+//
+// Codepoint
+// Characters are defined by unicode codepoints, e.g. 65 is
+// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
+// the hiragana for "ma".
+//
+// Glyph
+// A visual character shape (every codepoint is rendered as
+// some glyph)
+//
+// Glyph index
+// A font-specific integer ID representing a glyph
+//
+// Baseline
+// Glyph shapes are defined relative to a baseline, which is the
+// bottom of uppercase characters. Characters extend both above
+// and below the baseline.
+//
+// Current Point
+// As you draw text to the screen, you keep track of a "current point"
+// which is the origin of each character. The current point's vertical
+// position is the baseline. Even "baked fonts" use this model.
+//
+// Vertical Font Metrics
+// The vertical qualities of the font, used to vertically position
+// and space the characters. See docs for stbtt_GetFontVMetrics.
+//
+// Font Size in Pixels or Points
+// The preferred interface for specifying font sizes in stb_truetype
+// is to specify how tall the font's vertical extent should be in pixels.
+// If that sounds good enough, skip the next paragraph.
+//
+// Most font APIs instead use "points", which are a common typographic
+// measurement for describing font size, defined as 72 points per inch.
+// stb_truetype provides a point API for compatibility. However, true
+// "per inch" conventions don't make much sense on computer displays
+// since they different monitors have different number of pixels per
+// inch. For example, Windows traditionally uses a convention that
+// there are 96 pixels per inch, thus making 'inch' measurements have
+// nothing to do with inches, and thus effectively defining a point to
+// be 1.333 pixels. Additionally, the TrueType font data provides
+// an explicit scale factor to scale a given font's glyphs to points,
+// but the author has observed that this scale factor is often wrong
+// for non-commercial fonts, thus making fonts scaled in points
+// according to the TrueType spec incoherently sized in practice.
+//
+// ADVANCED USAGE
+//
+// Quality:
+//
+// - Use the functions with Subpixel at the end to allow your characters
+// to have subpixel positioning. Since the font is anti-aliased, not
+// hinted, this is very import for quality. (This is not possible with
+// baked fonts.)
+//
+// - Kerning is now supported, and if you're supporting subpixel rendering
+// then kerning is worth using to give your text a polished look.
+//
+// Performance:
+//
+// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
+// if you don't do this, stb_truetype is forced to do the conversion on
+// every call.
+//
+// - There are a lot of memory allocations. We should modify it to take
+// a temp buffer and allocate from the temp buffer (without freeing),
+// should help performance a lot.
+//
+// NOTES
+//
+// The system uses the raw data found in the .ttf file without changing it
+// and without building auxiliary data structures. This is a bit inefficient
+// on little-endian systems (the data is big-endian), but assuming you're
+// caching the bitmaps or glyph shapes this shouldn't be a big deal.
+//
+// It appears to be very hard to programmatically determine what font a
+// given file is in a general way. I provide an API for this, but I don't
+// recommend it.
+//
+//
+// SOURCE STATISTICS (based on v0.6c, 2050 LOC)
+//
+// Documentation & header file 520 LOC \___ 660 LOC documentation
+// Sample code 140 LOC /
+// Truetype parsing 620 LOC ---- 620 LOC TrueType
+// Software rasterization 240 LOC \ .
+// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation
+// Bitmap management 100 LOC /
+// Baked bitmap interface 70 LOC /
+// Font name matching & access 150 LOC ---- 150
+// C runtime library abstraction 60 LOC ---- 60
+//
+//
+// PERFORMANCE MEASUREMENTS FOR 1.06:
+//
+// 32-bit 64-bit
+// Previous release: 8.83 s 7.68 s
+// Pool allocations: 7.72 s 6.34 s
+// Inline sort : 6.54 s 5.65 s
+// New rasterizer : 5.63 s 5.00 s
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+////
+//// SAMPLE PROGRAMS
+////
+//
+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
+//
+#if 0
+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
+#include "stb_truetype.h"
+
+unsigned char ttf_buffer[1<<20];
+unsigned char temp_bitmap[512*512];
+
+stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
+GLuint ftex;
+
+void my_stbtt_initfont(void)
+{
+ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
+ stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
+ // can free ttf_buffer at this point
+ glGenTextures(1, &ftex);
+ glBindTexture(GL_TEXTURE_2D, ftex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
+ // can free temp_bitmap at this point
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+}
+
+void my_stbtt_print(float x, float y, char *text)
+{
+ // assume orthographic projection with units = screen pixels, origin at top left
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, ftex);
+ glBegin(GL_QUADS);
+ while (*text) {
+ if (*text >= 32 && *text < 128) {
+ stbtt_aligned_quad q;
+ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
+ }
+ ++text;
+ }
+ glEnd();
+}
+#endif
+//
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// Complete program (this compiles): get a single bitmap, print as ASCII art
+//
+#if 0
+#include <stdio.h>
+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
+#include "stb_truetype.h"
+
+char ttf_buffer[1<<25];
+
+int main(int argc, char **argv)
+{
+ stbtt_fontinfo font;
+ unsigned char *bitmap;
+ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
+
+ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
+
+ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
+ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
+
+ for (j=0; j < h; ++j) {
+ for (i=0; i < w; ++i)
+ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
+ putchar('\n');
+ }
+ return 0;
+}
+#endif
+//
+// Output:
+//
+// .ii.
+// @@@@@@.
+// V@Mio@@o
+// :i. V@V
+// :oM@@M
+// :@@@MM@M
+// @@o o@M
+// :@@. M@M
+// @@@o@@@@
+// :M@@V:@@.
+//
+//////////////////////////////////////////////////////////////////////////////
+//
+// Complete program: print "Hello World!" banner, with bugs
+//
+#if 0
+char buffer[24<<20];
+unsigned char screen[20][79];
+
+int main(int arg, char **argv)
+{
+ stbtt_fontinfo font;
+ int i,j,ascent,baseline,ch=0;
+ float scale, xpos=2; // leave a little padding in case the character extends left
+ char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
+
+ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
+ stbtt_InitFont(&font, buffer, 0);
+
+ scale = stbtt_ScaleForPixelHeight(&font, 15);
+ stbtt_GetFontVMetrics(&font, &ascent,0,0);
+ baseline = (int) (ascent*scale);
+
+ while (text[ch]) {
+ int advance,lsb,x0,y0,x1,y1;
+ float x_shift = xpos - (float) floor(xpos);
+ stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
+ stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
+ stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
+ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
+ // because this API is really for baking character bitmaps into textures. if you want to render
+ // a sequence of characters, you really need to render each bitmap to a temp buffer, then
+ // "alpha blend" that into the working buffer
+ xpos += (advance * scale);
+ if (text[ch+1])
+ xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
+ ++ch;
+ }
+
+ for (j=0; j < 20; ++j) {
+ for (i=0; i < 78; ++i)
+ putchar(" .:ioVM@"[screen[j][i]>>5]);
+ putchar('\n');
+ }
+
+ return 0;
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+////
+//// INTEGRATION WITH YOUR CODEBASE
+////
+//// The following sections allow you to supply alternate definitions
+//// of C library functions used by stb_truetype.
+
+#ifdef STB_TRUETYPE_IMPLEMENTATION
+ // #define your own (u)stbtt_int8/16/32 before including to override this
+ #ifndef stbtt_uint8
+ typedef unsigned char stbtt_uint8;
+ typedef signed char stbtt_int8;
+ typedef unsigned short stbtt_uint16;
+ typedef signed short stbtt_int16;
+ typedef unsigned int stbtt_uint32;
+ typedef signed int stbtt_int32;
+ #endif
+
+ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
+ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
+
+ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
+ #ifndef STBTT_ifloor
+ #include <math.h>
+ #define STBTT_ifloor(x) ((int) floor(x))
+ #define STBTT_iceil(x) ((int) ceil(x))
+ #endif
+
+ #ifndef STBTT_sqrt
+ #include <math.h>
+ #define STBTT_sqrt(x) sqrt(x)
+ #endif
+
+ #ifndef STBTT_fabs
+ #include <math.h>
+ #define STBTT_fabs(x) fabs(x)
+ #endif
+
+ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
+ #ifndef STBTT_malloc
+ #include <stdlib.h>
+ #define STBTT_malloc(x,u) ((void)(u),malloc(x))
+ #define STBTT_free(x,u) ((void)(u),free(x))
+ #endif
+
+ #ifndef STBTT_assert
+ #include <assert.h>
+ #define STBTT_assert(x) assert(x)
+ #endif
+
+ #ifndef STBTT_strlen
+ #include <string.h>
+ #define STBTT_strlen(x) strlen(x)
+ #endif
+
+ #ifndef STBTT_memcpy
+ #include <memory.h>
+ #define STBTT_memcpy memcpy
+ #define STBTT_memset memset
+ #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+////
+//// INTERFACE
+////
+////
+
+#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
+#define __STB_INCLUDE_STB_TRUETYPE_H__
+
+#ifdef STBTT_STATIC
+#define STBTT_DEF static
+#else
+#define STBTT_DEF extern
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// TEXTURE BAKING API
+//
+// If you use this API, you only have to call two functions ever.
+//
+
+typedef struct
+{
+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
+ float xoff,yoff,xadvance;
+} stbtt_bakedchar;
+
+STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
+ float pixel_height, // height of font in pixels
+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
+ int first_char, int num_chars, // characters to bake
+ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
+// if return is positive, the first unused row of the bitmap
+// if return is negative, returns the negative of the number of characters that fit
+// if return is 0, no characters fit and no rows were used
+// This uses a very crappy packing.
+
+typedef struct
+{
+ float x0,y0,s0,t0; // top-left
+ float x1,y1,s1,t1; // bottom-right
+} stbtt_aligned_quad;
+
+STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
+ int char_index, // character to display
+ float *xpos, float *ypos, // pointers to current position in screen pixel space
+ stbtt_aligned_quad *q, // output: quad to draw
+ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
+// Call GetBakedQuad with char_index = 'character - first_char', and it
+// creates the quad you need to draw and advances the current position.
+//
+// The coordinate system used assumes y increases downwards.
+//
+// Characters will extend both above and below the current position;
+// see discussion of "BASELINE" above.
+//
+// It's inefficient; you might want to c&p it and optimize it.
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// NEW TEXTURE BAKING API
+//
+// This provides options for packing multiple fonts into one atlas, not
+// perfectly but better than nothing.
+
+typedef struct
+{
+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
+ float xoff,yoff,xadvance;
+ float xoff2,yoff2;
+} stbtt_packedchar;
+
+typedef struct stbtt_pack_context stbtt_pack_context;
+typedef struct stbtt_fontinfo stbtt_fontinfo;
+#ifndef STB_RECT_PACK_VERSION
+typedef struct stbrp_rect stbrp_rect;
+#endif
+
+STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
+// Initializes a packing context stored in the passed-in stbtt_pack_context.
+// Future calls using this context will pack characters into the bitmap passed
+// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is
+// the distance from one row to the next (or 0 to mean they are packed tightly
+// together). "padding" is the amount of padding to leave between each
+// character (normally you want '1' for bitmaps you'll use as textures with
+// bilinear filtering).
+//
+// Returns 0 on failure, 1 on success.
+
+STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
+// Cleans up the packing context and frees all memory.
+
+#define STBTT_POINT_SIZE(x) (-(x))
+
+STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+ int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
+// Creates character bitmaps from the font_index'th font found in fontdata (use
+// font_index=0 if you don't know what that is). It creates num_chars_in_range
+// bitmaps for characters with unicode values starting at first_unicode_char_in_range
+// and increasing. Data for how to render them is stored in chardata_for_range;
+// pass these to stbtt_GetPackedQuad to get back renderable quads.
+//
+// font_size is the full height of the character from ascender to descender,
+// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
+// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
+// and pass that result as 'font_size':
+// ..., 20 , ... // font max minus min y is 20 pixels tall
+// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
+
+typedef struct
+{
+ float font_size;
+ int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint
+ int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints
+ int num_chars;
+ stbtt_packedchar *chardata_for_range; // output
+ unsigned char h_oversample, v_oversample; // don't set these, they're used internally
+} stbtt_pack_range;
+
+STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
+// Creates character bitmaps from multiple ranges of characters stored in
+// ranges. This will usually create a better-packed bitmap than multiple
+// calls to stbtt_PackFontRange. Note that you can call this multiple
+// times within a single PackBegin/PackEnd.
+
+STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
+// Oversampling a font increases the quality by allowing higher-quality subpixel
+// positioning, and is especially valuable at smaller text sizes.
+//
+// This function sets the amount of oversampling for all following calls to
+// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
+// pack context. The default (no oversampling) is achieved by h_oversample=1
+// and v_oversample=1. The total number of pixels required is
+// h_oversample*v_oversample larger than the default; for example, 2x2
+// oversampling requires 4x the storage of 1x1. For best results, render
+// oversampled textures with bilinear filtering. Look at the readme in
+// stb/tests/oversample for information about oversampled fonts
+//
+// To use with PackFontRangesGather etc., you must set it before calls
+// call to PackFontRangesGatherRects.
+
+STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above
+ int char_index, // character to display
+ float *xpos, float *ypos, // pointers to current position in screen pixel space
+ stbtt_aligned_quad *q, // output: quad to draw
+ int align_to_integer);
+
+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
+STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
+// Calling these functions in sequence is roughly equivalent to calling
+// stbtt_PackFontRanges(). If you more control over the packing of multiple
+// fonts, or if you want to pack custom data into a font texture, take a look
+// at the source to of stbtt_PackFontRanges() and create a custom version
+// using these functions, e.g. call GatherRects multiple times,
+// building up a single array of rects, then call PackRects once,
+// then call RenderIntoRects repeatedly. This may result in a
+// better packing than calling PackFontRanges multiple times
+// (or it may not).
+
+// this is an opaque structure that you shouldn't mess with which holds
+// all the context needed from PackBegin to PackEnd.
+struct stbtt_pack_context {
+ void *user_allocator_context;
+ void *pack_info;
+ int width;
+ int height;
+ int stride_in_bytes;
+ int padding;
+ unsigned int h_oversample, v_oversample;
+ unsigned char *pixels;
+ void *nodes;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// FONT LOADING
+//
+//
+
+STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
+// Each .ttf/.ttc file may have more than one font. Each font has a sequential
+// index number starting from 0. Call this function to get the font offset for
+// a given index; it returns -1 if the index is out of range. A regular .ttf
+// file will only define one font and it always be at offset 0, so it will
+// return '0' for index 0, and -1 for all other indices. You can just skip
+// this step if you know it's that kind of font.
+
+
+// The following structure is defined publically so you can declare one on
+// the stack or as a global or etc, but you should treat it as opaque.
+struct stbtt_fontinfo
+{
+ void * userdata;
+ unsigned char * data; // pointer to .ttf file
+ int fontstart; // offset of start of font
+
+ int numGlyphs; // number of glyphs, needed for range checking
+
+ int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
+ int index_map; // a cmap mapping for our chosen character encoding
+ int indexToLocFormat; // format needed to map from glyph index to glyph
+};
+
+STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
+// Given an offset into the file that defines a font, this function builds
+// the necessary cached info for the rest of the system. You must allocate
+// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
+// need to do anything special to free it, because the contents are pure
+// value data with no additional data structures. Returns 0 on failure.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// CHARACTER TO GLYPH-INDEX CONVERSIOn
+
+STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
+// If you're going to perform multiple operations on the same character
+// and you want a speed-up, call this function with the character you're
+// going to process, then use glyph-based functions instead of the
+// codepoint-based functions.
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// CHARACTER PROPERTIES
+//
+
+STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
+// computes a scale factor to produce a font whose "height" is 'pixels' tall.
+// Height is measured as the distance from the highest ascender to the lowest
+// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
+// and computing:
+// scale = pixels / (ascent - descent)
+// so if you prefer to measure height by the ascent only, use a similar calculation.
+
+STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
+// computes a scale factor to produce a font whose EM size is mapped to
+// 'pixels' tall. This is probably what traditional APIs compute, but
+// I'm not positive.
+
+STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
+// ascent is the coordinate above the baseline the font extends; descent
+// is the coordinate below the baseline the font extends (i.e. it is typically negative)
+// lineGap is the spacing between one row's descent and the next row's ascent...
+// so you should advance the vertical position by "*ascent - *descent + *lineGap"
+// these are expressed in unscaled coordinates, so you must multiply by
+// the scale factor for a given size
+
+STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
+// the bounding box around all possible characters
+
+STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
+// leftSideBearing is the offset from the current horizontal position to the left edge of the character
+// advanceWidth is the offset from the current horizontal position to the next horizontal position
+// these are expressed in unscaled coordinates
+
+STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
+// an additional amount to add to the 'advance' value between ch1 and ch2
+
+STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
+// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
+
+STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
+STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
+// as above, but takes one or more glyph indices for greater efficiency
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// GLYPH SHAPES (you probably don't need these, but they have to go before
+// the bitmaps for C declaration-order reasons)
+//
+
+#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
+ enum {
+ STBTT_vmove=1,
+ STBTT_vline,
+ STBTT_vcurve
+ };
+#endif
+
+#ifndef stbtt_vertex // you can predefine this to use different values
+ // (we share this with other code at RAD)
+ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
+ typedef struct
+ {
+ stbtt_vertex_type x,y,cx,cy;
+ unsigned char type,padding;
+ } stbtt_vertex;
+#endif
+
+STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
+// returns non-zero if nothing is drawn for this glyph
+
+STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
+STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
+// returns # of vertices and fills *vertices with the pointer to them
+// these are expressed in "unscaled" coordinates
+//
+// The shape is a series of countours. Each one starts with
+// a STBTT_moveto, then consists of a series of mixed
+// STBTT_lineto and STBTT_curveto segments. A lineto
+// draws a line from previous endpoint to its x,y; a curveto
+// draws a quadratic bezier from previous endpoint to
+// its x,y, using cx,cy as the bezier control point.
+
+STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
+// frees the data allocated above
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// BITMAP RENDERING
+//
+
+STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
+// frees the bitmap allocated below
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
+// allocates a large-enough single-channel 8bpp bitmap and renders the
+// specified character/glyph at the specified scale into it, with
+// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
+// *width & *height are filled out with the width & height of the bitmap,
+// which is stored left-to-right, top-to-bottom.
+//
+// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
+// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
+// shift for the character
+
+STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
+// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
+// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
+// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
+// width and height and positioning info for it first.
+
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
+// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
+// shift for the character
+
+STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
+// get the bbox of the bitmap centered around the glyph origin; so the
+// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
+// the bitmap top left is (leftSideBearing*scale,iy0).
+// (Note that the bitmap uses y-increases-down, but the shape uses
+// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
+
+STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
+// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
+// shift for the character
+
+// the following functions are equivalent to the above functions, but operate
+// on glyph indices instead of Unicode codepoints (for efficiency)
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
+STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
+STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
+STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
+
+
+// @TODO: don't expose this structure
+typedef struct
+{
+ int w,h,stride;
+ unsigned char *pixels;
+} stbtt__bitmap;
+
+// rasterize a shape with quadratic beziers into a bitmap
+STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into
+ float flatness_in_pixels, // allowable error of curve in pixels
+ stbtt_vertex *vertices, // array of vertices defining shape
+ int num_verts, // number of vertices in above array
+ float scale_x, float scale_y, // scale applied to input vertices
+ float shift_x, float shift_y, // translation applied to input vertices
+ int x_off, int y_off, // another translation applied to input
+ int invert, // if non-zero, vertically flip shape
+ void *userdata); // context for to STBTT_MALLOC
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Finding the right font...
+//
+// You should really just solve this offline, keep your own tables
+// of what font is what, and don't try to get it out of the .ttf file.
+// That's because getting it out of the .ttf file is really hard, because
+// the names in the file can appear in many possible encodings, in many
+// possible languages, and e.g. if you need a case-insensitive comparison,
+// the details of that depend on the encoding & language in a complex way
+// (actually underspecified in truetype, but also gigantic).
+//
+// But you can use the provided functions in two possible ways:
+// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
+// unicode-encoded names to try to find the font you want;
+// you can run this before calling stbtt_InitFont()
+//
+// stbtt_GetFontNameString() lets you get any of the various strings
+// from the file yourself and do your own comparisons on them.
+// You have to have called stbtt_InitFont() first.
+
+
+STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
+// returns the offset (not index) of the font that matches, or -1 if none
+// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
+// if you use any other flag, use a font name like "Arial"; this checks
+// the 'macStyle' header field; i don't know if fonts set this consistently
+#define STBTT_MACSTYLE_DONTCARE 0
+#define STBTT_MACSTYLE_BOLD 1
+#define STBTT_MACSTYLE_ITALIC 2
+#define STBTT_MACSTYLE_UNDERSCORE 4
+#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
+
+STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
+// returns 1/0 whether the first string interpreted as utf8 is identical to
+// the second string interpreted as big-endian utf16... useful for strings from next func
+
+STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
+// returns the string (which may be big-endian double byte, e.g. for unicode)
+// and puts the length in bytes in *length.
+//
+// some of the values for the IDs are below; for more see the truetype spec:
+// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
+// http://www.microsoft.com/typography/otspec/name.htm
+
+enum { // platformID
+ STBTT_PLATFORM_ID_UNICODE =0,
+ STBTT_PLATFORM_ID_MAC =1,
+ STBTT_PLATFORM_ID_ISO =2,
+ STBTT_PLATFORM_ID_MICROSOFT =3
+};
+
+enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
+ STBTT_UNICODE_EID_UNICODE_1_0 =0,
+ STBTT_UNICODE_EID_UNICODE_1_1 =1,
+ STBTT_UNICODE_EID_ISO_10646 =2,
+ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
+ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
+};
+
+enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
+ STBTT_MS_EID_SYMBOL =0,
+ STBTT_MS_EID_UNICODE_BMP =1,
+ STBTT_MS_EID_SHIFTJIS =2,
+ STBTT_MS_EID_UNICODE_FULL =10
+};
+
+enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
+ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
+ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
+ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
+ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7
+};
+
+enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
+ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
+ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
+ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
+ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
+ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
+ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
+ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D
+};
+
+enum { // languageID for STBTT_PLATFORM_ID_MAC
+ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
+ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
+ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
+ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
+ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
+ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
+ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __STB_INCLUDE_STB_TRUETYPE_H__
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+////
+//// IMPLEMENTATION
+////
+////
+
+#ifdef STB_TRUETYPE_IMPLEMENTATION
+
+#ifndef STBTT_MAX_OVERSAMPLE
+#define STBTT_MAX_OVERSAMPLE 8
+#endif
+
+#if STBTT_MAX_OVERSAMPLE > 255
+#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
+#endif
+
+typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
+
+#ifndef STBTT_RASTERIZER_VERSION
+#define STBTT_RASTERIZER_VERSION 2
+#endif
+
+#ifdef _MSC_VER
+#define STBTT__NOTUSED(v) (void)(v)
+#else
+#define STBTT__NOTUSED(v) (void)sizeof(v)
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+//
+// accessors to parse data from file
+//
+
+// on platforms that don't allow misaligned reads, if we want to allow
+// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
+
+#define ttBYTE(p) (* (stbtt_uint8 *) (p))
+#define ttCHAR(p) (* (stbtt_int8 *) (p))
+#define ttFixed(p) ttLONG(p)
+
+#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
+
+ #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
+ #define ttSHORT(p) (* (stbtt_int16 *) (p))
+ #define ttULONG(p) (* (stbtt_uint32 *) (p))
+ #define ttLONG(p) (* (stbtt_int32 *) (p))
+
+#else
+
+ static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
+ static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
+ static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
+ static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
+
+#endif
+
+#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
+#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
+
+static int stbtt__isfont(const stbtt_uint8 *font)
+{
+ // check the version number
+ if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1
+ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
+ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
+ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
+ return 0;
+}
+
+// @OPTIMIZE: binary search
+static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
+{
+ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
+ stbtt_uint32 tabledir = fontstart + 12;
+ stbtt_int32 i;
+ for (i=0; i < num_tables; ++i) {
+ stbtt_uint32 loc = tabledir + 16*i;
+ if (stbtt_tag(data+loc+0, tag))
+ return ttULONG(data+loc+8);
+ }
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
+{
+ // if it's just a font, there's only one valid index
+ if (stbtt__isfont(font_collection))
+ return index == 0 ? 0 : -1;
+
+ // check if it's a TTC
+ if (stbtt_tag(font_collection, "ttcf")) {
+ // version 1?
+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
+ stbtt_int32 n = ttLONG(font_collection+8);
+ if (index >= n)
+ return -1;
+ return ttULONG(font_collection+12+index*4);
+ }
+ }
+ return -1;
+}
+
+STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
+{
+ stbtt_uint8 *data = (stbtt_uint8 *) data2;
+ stbtt_uint32 cmap, t;
+ stbtt_int32 i,numTables;
+
+ info->data = data;
+ info->fontstart = fontstart;
+
+ cmap = stbtt__find_table(data, fontstart, "cmap"); // required
+ info->loca = stbtt__find_table(data, fontstart, "loca"); // required
+ info->head = stbtt__find_table(data, fontstart, "head"); // required
+ info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
+ info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
+ info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
+ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
+ return 0;
+
+ t = stbtt__find_table(data, fontstart, "maxp");
+ if (t)
+ info->numGlyphs = ttUSHORT(data+t+4);
+ else
+ info->numGlyphs = 0xffff;
+
+ // find a cmap encoding table we understand *now* to avoid searching
+ // later. (todo: could make this installable)
+ // the same regardless of glyph.
+ numTables = ttUSHORT(data + cmap + 2);
+ info->index_map = 0;
+ for (i=0; i < numTables; ++i) {
+ stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
+ // find an encoding we understand:
+ switch(ttUSHORT(data+encoding_record)) {
+ case STBTT_PLATFORM_ID_MICROSOFT:
+ switch (ttUSHORT(data+encoding_record+2)) {
+ case STBTT_MS_EID_UNICODE_BMP:
+ case STBTT_MS_EID_UNICODE_FULL:
+ // MS/Unicode
+ info->index_map = cmap + ttULONG(data+encoding_record+4);
+ break;
+ }
+ break;
+ case STBTT_PLATFORM_ID_UNICODE:
+ // Mac/iOS has these
+ // all the encodingIDs are unicode, so we don't bother to check it
+ info->index_map = cmap + ttULONG(data+encoding_record+4);
+ break;
+ }
+ }
+ if (info->index_map == 0)
+ return 0;
+
+ info->indexToLocFormat = ttUSHORT(data+info->head + 50);
+ return 1;
+}
+
+STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
+{
+ stbtt_uint8 *data = info->data;
+ stbtt_uint32 index_map = info->index_map;
+
+ stbtt_uint16 format = ttUSHORT(data + index_map + 0);
+ if (format == 0) { // apple byte encoding
+ stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
+ if (unicode_codepoint < bytes-6)
+ return ttBYTE(data + index_map + 6 + unicode_codepoint);
+ return 0;
+ } else if (format == 6) {
+ stbtt_uint32 first = ttUSHORT(data + index_map + 6);
+ stbtt_uint32 count = ttUSHORT(data + index_map + 8);
+ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
+ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
+ return 0;
+ } else if (format == 2) {
+ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
+ return 0;
+ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
+ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
+ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
+ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
+ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
+
+ // do a binary search of the segments
+ stbtt_uint32 endCount = index_map + 14;
+ stbtt_uint32 search = endCount;
+
+ if (unicode_codepoint > 0xffff)
+ return 0;
+
+ // they lie from endCount .. endCount + segCount
+ // but searchRange is the nearest power of two, so...
+ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
+ search += rangeShift*2;
+
+ // now decrement to bias correctly to find smallest
+ search -= 2;
+ while (entrySelector) {
+ stbtt_uint16 end;
+ searchRange >>= 1;
+ end = ttUSHORT(data + search + searchRange*2);
+ if (unicode_codepoint > end)
+ search += searchRange*2;
+ --entrySelector;
+ }
+ search += 2;
+
+ {
+ stbtt_uint16 offset, start;
+ stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
+
+ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
+ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
+ if (unicode_codepoint < start)
+ return 0;
+
+ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
+ if (offset == 0)
+ return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
+
+ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
+ }
+ } else if (format == 12 || format == 13) {
+ stbtt_uint32 ngroups = ttULONG(data+index_map+12);
+ stbtt_int32 low,high;
+ low = 0; high = (stbtt_int32)ngroups;
+ // Binary search the right group.
+ while (low < high) {
+ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
+ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
+ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
+ if ((stbtt_uint32) unicode_codepoint < start_char)
+ high = mid;
+ else if ((stbtt_uint32) unicode_codepoint > end_char)
+ low = mid+1;
+ else {
+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
+ if (format == 12)
+ return start_glyph + unicode_codepoint-start_char;
+ else // format == 13
+ return start_glyph;
+ }
+ }
+ return 0; // not found
+ }
+ // @TODO
+ STBTT_assert(0);
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
+{
+ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
+}
+
+static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
+{
+ v->type = type;
+ v->x = (stbtt_int16) x;
+ v->y = (stbtt_int16) y;
+ v->cx = (stbtt_int16) cx;
+ v->cy = (stbtt_int16) cy;
+}
+
+static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
+{
+ int g1,g2;
+
+ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
+ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
+
+ if (info->indexToLocFormat == 0) {
+ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
+ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
+ } else {
+ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
+ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
+ }
+
+ return g1==g2 ? -1 : g1; // if length is 0, return -1
+}
+
+STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
+{
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 0;
+
+ if (x0) *x0 = ttSHORT(info->data + g + 2);
+ if (y0) *y0 = ttSHORT(info->data + g + 4);
+ if (x1) *x1 = ttSHORT(info->data + g + 6);
+ if (y1) *y1 = ttSHORT(info->data + g + 8);
+ return 1;
+}
+
+STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
+{
+ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
+}
+
+STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
+{
+ stbtt_int16 numberOfContours;
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+ if (g < 0) return 1;
+ numberOfContours = ttSHORT(info->data + g);
+ return numberOfContours == 0;
+}
+
+static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
+ stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
+{
+ if (start_off) {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
+ } else {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
+ else
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
+ }
+ return num_vertices;
+}
+
+STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
+{
+ stbtt_int16 numberOfContours;
+ stbtt_uint8 *endPtsOfContours;
+ stbtt_uint8 *data = info->data;
+ stbtt_vertex *vertices=0;
+ int num_vertices=0;
+ int g = stbtt__GetGlyfOffset(info, glyph_index);
+
+ *pvertices = NULL;
+
+ if (g < 0) return 0;
+
+ numberOfContours = ttSHORT(data + g);
+
+ if (numberOfContours > 0) {
+ stbtt_uint8 flags=0,flagcount;
+ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
+ stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
+ stbtt_uint8 *points;
+ endPtsOfContours = (data + g + 10);
+ ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
+ points = data + g + 10 + numberOfContours * 2 + 2 + ins;
+
+ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
+
+ m = n + 2*numberOfContours; // a loose bound on how many vertices we might need
+ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
+ if (vertices == 0)
+ return 0;
+
+ next_move = 0;
+ flagcount=0;
+
+ // in first pass, we load uninterpreted data into the allocated array
+ // above, shifted to the end of the array so we won't overwrite it when
+ // we create our final data starting from the front
+
+ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
+
+ // first load flags
+
+ for (i=0; i < n; ++i) {
+ if (flagcount == 0) {
+ flags = *points++;
+ if (flags & 8)
+ flagcount = *points++;
+ } else
+ --flagcount;
+ vertices[off+i].type = flags;
+ }
+
+ // now load x coordinates
+ x=0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ if (flags & 2) {
+ stbtt_int16 dx = *points++;
+ x += (flags & 16) ? dx : -dx; // ???
+ } else {
+ if (!(flags & 16)) {
+ x = x + (stbtt_int16) (points[0]*256 + points[1]);
+ points += 2;
+ }
+ }
+ vertices[off+i].x = (stbtt_int16) x;
+ }
+
+ // now load y coordinates
+ y=0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ if (flags & 4) {
+ stbtt_int16 dy = *points++;
+ y += (flags & 32) ? dy : -dy; // ???
+ } else {
+ if (!(flags & 32)) {
+ y = y + (stbtt_int16) (points[0]*256 + points[1]);
+ points += 2;
+ }
+ }
+ vertices[off+i].y = (stbtt_int16) y;
+ }
+
+ // now convert them to our format
+ num_vertices=0;
+ sx = sy = cx = cy = scx = scy = 0;
+ for (i=0; i < n; ++i) {
+ flags = vertices[off+i].type;
+ x = (stbtt_int16) vertices[off+i].x;
+ y = (stbtt_int16) vertices[off+i].y;
+
+ if (next_move == i) {
+ if (i != 0)
+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+
+ // now start the new one
+ start_off = !(flags & 1);
+ if (start_off) {
+ // if we start off with an off-curve point, then when we need to find a point on the curve
+ // where we can start, and we need to save some state for when we wraparound.
+ scx = x;
+ scy = y;
+ if (!(vertices[off+i+1].type & 1)) {
+ // next point is also a curve point, so interpolate an on-point curve
+ sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
+ sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
+ } else {
+ // otherwise just use the next point as our start point
+ sx = (stbtt_int32) vertices[off+i+1].x;
+ sy = (stbtt_int32) vertices[off+i+1].y;
+ ++i; // we're using point i+1 as the starting point, so skip it
+ }
+ } else {
+ sx = x;
+ sy = y;
+ }
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
+ was_off = 0;
+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
+ ++j;
+ } else {
+ if (!(flags & 1)) { // if it's a curve
+ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
+ cx = x;
+ cy = y;
+ was_off = 1;
+ } else {
+ if (was_off)
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
+ else
+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
+ was_off = 0;
+ }
+ }
+ }
+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
+ } else if (numberOfContours == -1) {
+ // Compound shapes.
+ int more = 1;
+ stbtt_uint8 *comp = data + g + 10;
+ num_vertices = 0;
+ vertices = 0;
+ while (more) {
+ stbtt_uint16 flags, gidx;
+ int comp_num_verts = 0, i;
+ stbtt_vertex *comp_verts = 0, *tmp = 0;
+ float mtx[6] = {1,0,0,1,0,0}, m, n;
+
+ flags = ttSHORT(comp); comp+=2;
+ gidx = ttSHORT(comp); comp+=2;
+
+ if (flags & 2) { // XY values
+ if (flags & 1) { // shorts
+ mtx[4] = ttSHORT(comp); comp+=2;
+ mtx[5] = ttSHORT(comp); comp+=2;
+ } else {
+ mtx[4] = ttCHAR(comp); comp+=1;
+ mtx[5] = ttCHAR(comp); comp+=1;
+ }
+ }
+ else {
+ // @TODO handle matching point
+ STBTT_assert(0);
+ }
+ if (flags & (1<<3)) { // WE_HAVE_A_SCALE
+ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[1] = mtx[2] = 0;
+ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[1] = mtx[2] = 0;
+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
+ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
+ }
+
+ // Find transformation scales.
+ m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
+ n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
+
+ // Get indexed glyph.
+ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
+ if (comp_num_verts > 0) {
+ // Transform vertices.
+ for (i = 0; i < comp_num_verts; ++i) {
+ stbtt_vertex* v = &comp_verts[i];
+ stbtt_vertex_type x,y;
+ x=v->x; y=v->y;
+ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+ x=v->cx; y=v->cy;
+ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
+ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+ }
+ // Append vertices.
+ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
+ if (!tmp) {
+ if (vertices) STBTT_free(vertices, info->userdata);
+ if (comp_verts) STBTT_free(comp_verts, info->userdata);
+ return 0;
+ }
+ if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
+ STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
+ if (vertices) STBTT_free(vertices, info->userdata);
+ vertices = tmp;
+ STBTT_free(comp_verts, info->userdata);
+ num_vertices += comp_num_verts;
+ }
+ // More components ?
+ more = flags & (1<<5);
+ }
+ } else if (numberOfContours < 0) {
+ // @TODO other compound variations?
+ STBTT_assert(0);
+ } else {
+ // numberOfCounters == 0, do nothing
+ }
+
+ *pvertices = vertices;
+ return num_vertices;
+}
+
+STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
+{
+ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
+ if (glyph_index < numOfLongHorMetrics) {
+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
+ } else {
+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
+ }
+}
+
+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
+{
+ stbtt_uint8 *data = info->data + info->kern;
+ stbtt_uint32 needle, straw;
+ int l, r, m;
+
+ // we only look at the first table. it must be 'horizontal' and format 0.
+ if (!info->kern)
+ return 0;
+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
+ return 0;
+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
+ return 0;
+
+ l = 0;
+ r = ttUSHORT(data+10) - 1;
+ needle = glyph1 << 16 | glyph2;
+ while (l <= r) {
+ m = (l + r) >> 1;
+ straw = ttULONG(data+18+(m*6)); // note: unaligned read
+ if (needle < straw)
+ r = m - 1;
+ else if (needle > straw)
+ l = m + 1;
+ else
+ return ttSHORT(data+22+(m*6));
+ }
+ return 0;
+}
+
+STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
+{
+ if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
+ return 0;
+ return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
+}
+
+STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
+{
+ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
+}
+
+STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
+{
+ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
+ if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
+ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
+}
+
+STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
+{
+ *x0 = ttSHORT(info->data + info->head + 36);
+ *y0 = ttSHORT(info->data + info->head + 38);
+ *x1 = ttSHORT(info->data + info->head + 40);
+ *y1 = ttSHORT(info->data + info->head + 42);
+}
+
+STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
+{
+ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
+ return (float) height / fheight;
+}
+
+STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
+{
+ int unitsPerEm = ttUSHORT(info->data + info->head + 18);
+ return pixels / unitsPerEm;
+}
+
+STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
+{
+ STBTT_free(v, info->userdata);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// antialiasing software rasterizer
+//
+
+STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
+ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
+ // e.g. space character
+ if (ix0) *ix0 = 0;
+ if (iy0) *iy0 = 0;
+ if (ix1) *ix1 = 0;
+ if (iy1) *iy1 = 0;
+ } else {
+ // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
+ if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
+ if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
+ if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
+ if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
+ }
+}
+
+STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
+}
+
+STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
+}
+
+STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
+{
+ stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Rasterizer
+
+typedef struct stbtt__hheap_chunk
+{
+ struct stbtt__hheap_chunk *next;
+} stbtt__hheap_chunk;
+
+typedef struct stbtt__hheap
+{
+ struct stbtt__hheap_chunk *head;
+ void *first_free;
+ int num_remaining_in_head_chunk;
+} stbtt__hheap;
+
+static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
+{
+ if (hh->first_free) {
+ void *p = hh->first_free;
+ hh->first_free = * (void **) p;
+ return p;
+ } else {
+ if (hh->num_remaining_in_head_chunk == 0) {
+ int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
+ stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
+ if (c == NULL)
+ return NULL;
+ c->next = hh->head;
+ hh->head = c;
+ hh->num_remaining_in_head_chunk = count;
+ }
+ --hh->num_remaining_in_head_chunk;
+ return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;
+ }
+}
+
+static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
+{
+ *(void **) p = hh->first_free;
+ hh->first_free = p;
+}
+
+static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
+{
+ stbtt__hheap_chunk *c = hh->head;
+ while (c) {
+ stbtt__hheap_chunk *n = c->next;
+ STBTT_free(c, userdata);
+ c = n;
+ }
+}
+
+typedef struct stbtt__edge {
+ float x0,y0, x1,y1;
+ int invert;
+} stbtt__edge;
+
+
+typedef struct stbtt__active_edge
+{
+ struct stbtt__active_edge *next;
+ #if STBTT_RASTERIZER_VERSION==1
+ int x,dx;
+ float ey;
+ int direction;
+ #elif STBTT_RASTERIZER_VERSION==2
+ float fx,fdx,fdy;
+ float direction;
+ float sy;
+ float ey;
+ #else
+ #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+ #endif
+} stbtt__active_edge;
+
+#if STBTT_RASTERIZER_VERSION == 1
+#define STBTT_FIXSHIFT 10
+#define STBTT_FIX (1 << STBTT_FIXSHIFT)
+#define STBTT_FIXMASK (STBTT_FIX-1)
+
+static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
+{
+ stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
+ if (!z) return z;
+
+ // round dx down to avoid overshooting
+ if (dxdy < 0)
+ z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
+ else
+ z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
+
+ z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
+ z->x -= off_x * STBTT_FIX;
+
+ z->ey = e->y1;
+ z->next = 0;
+ z->direction = e->invert ? 1 : -1;
+ return z;
+}
+#elif STBTT_RASTERIZER_VERSION == 2
+static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
+{
+ stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
+ STBTT_assert(z != NULL);
+ //STBTT_assert(e->y0 <= start_point);
+ if (!z) return z;
+ z->fdx = dxdy;
+ z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
+ z->fx = e->x0 + dxdy * (start_point - e->y0);
+ z->fx -= off_x;
+ z->direction = e->invert ? 1.0f : -1.0f;
+ z->sy = e->y0;
+ z->ey = e->y1;
+ z->next = 0;
+ return z;
+}
+#else
+#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+#endif
+
+#if STBTT_RASTERIZER_VERSION == 1
+// note: this routine clips fills that extend off the edges... ideally this
+// wouldn't happen, but it could happen if the truetype glyph bounding boxes
+// are wrong, or if the user supplies a too-small bitmap
+static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
+{
+ // non-zero winding fill
+ int x0=0, w=0;
+
+ while (e) {
+ if (w == 0) {
+ // if we're currently at zero, we need to record the edge start point
+ x0 = e->x; w += e->direction;
+ } else {
+ int x1 = e->x; w += e->direction;
+ // if we went to zero, we need to draw
+ if (w == 0) {
+ int i = x0 >> STBTT_FIXSHIFT;
+ int j = x1 >> STBTT_FIXSHIFT;
+
+ if (i < len && j >= 0) {
+ if (i == j) {
+ // x0,x1 are the same pixel, so compute combined coverage
+ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
+ } else {
+ if (i >= 0) // add antialiasing for x0
+ scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
+ else
+ i = -1; // clip
+
+ if (j < len) // add antialiasing for x1
+ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
+ else
+ j = len; // clip
+
+ for (++i; i < j; ++i) // fill pixels between x0 and x1
+ scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
+ }
+ }
+ }
+ }
+
+ e = e->next;
+ }
+}
+
+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
+{
+ stbtt__hheap hh = { 0, 0, 0 };
+ stbtt__active_edge *active = NULL;
+ int y,j=0;
+ int max_weight = (255 / vsubsample); // weight per vertical scanline
+ int s; // vertical subsample index
+ unsigned char scanline_data[512], *scanline;
+
+ if (result->w > 512)
+ scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
+ else
+ scanline = scanline_data;
+
+ y = off_y * vsubsample;
+ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
+
+ while (j < result->h) {
+ STBTT_memset(scanline, 0, result->w);
+ for (s=0; s < vsubsample; ++s) {
+ // find center of pixel for this scanline
+ float scan_y = y + 0.5f;
+ stbtt__active_edge **step = &active;
+
+ // update all active edges;
+ // remove all active edges that terminate before the center of this scanline
+ while (*step) {
+ stbtt__active_edge * z = *step;
+ if (z->ey <= scan_y) {
+ *step = z->next; // delete from list
+ STBTT_assert(z->direction);
+ z->direction = 0;
+ stbtt__hheap_free(&hh, z);
+ } else {
+ z->x += z->dx; // advance to position for current scanline
+ step = &((*step)->next); // advance through list
+ }
+ }
+
+ // resort the list if needed
+ for(;;) {
+ int changed=0;
+ step = &active;
+ while (*step && (*step)->next) {
+ if ((*step)->x > (*step)->next->x) {
+ stbtt__active_edge *t = *step;
+ stbtt__active_edge *q = t->next;
+
+ t->next = q->next;
+ q->next = t;
+ *step = q;
+ changed = 1;
+ }
+ step = &(*step)->next;
+ }
+ if (!changed) break;
+ }
+
+ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
+ while (e->y0 <= scan_y) {
+ if (e->y1 > scan_y) {
+ stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
+ if (z != NULL) {
+ // find insertion point
+ if (active == NULL)
+ active = z;
+ else if (z->x < active->x) {
+ // insert at front
+ z->next = active;
+ active = z;
+ } else {
+ // find thing to insert AFTER
+ stbtt__active_edge *p = active;
+ while (p->next && p->next->x < z->x)
+ p = p->next;
+ // at this point, p->next->x is NOT < z->x
+ z->next = p->next;
+ p->next = z;
+ }
+ }
+ }
+ ++e;
+ }
+
+ // now process all active edges in XOR fashion
+ if (active)
+ stbtt__fill_active_edges(scanline, result->w, active, max_weight);
+
+ ++y;
+ }
+ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
+ ++j;
+ }
+
+ stbtt__hheap_cleanup(&hh, userdata);
+
+ if (scanline != scanline_data)
+ STBTT_free(scanline, userdata);
+}
+
+#elif STBTT_RASTERIZER_VERSION == 2
+
+// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
+// (i.e. it has already been clipped to those)
+static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
+{
+ if (y0 == y1) return;
+ STBTT_assert(y0 < y1);
+ STBTT_assert(e->sy <= e->ey);
+ if (y0 > e->ey) return;
+ if (y1 < e->sy) return;
+ if (y0 < e->sy) {
+ x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
+ y0 = e->sy;
+ }
+ if (y1 > e->ey) {
+ x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
+ y1 = e->ey;
+ }
+
+ if (x0 == x)
+ STBTT_assert(x1 <= x+1);
+ else if (x0 == x+1)
+ STBTT_assert(x1 >= x);
+ else if (x0 <= x)
+ STBTT_assert(x1 <= x);
+ else if (x0 >= x+1)
+ STBTT_assert(x1 >= x+1);
+ else
+ STBTT_assert(x1 >= x && x1 <= x+1);
+
+ if (x0 <= x && x1 <= x)
+ scanline[x] += e->direction * (y1-y0);
+ else if (x0 >= x+1 && x1 >= x+1)
+ ;
+ else {
+ STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
+ scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
+ }
+}
+
+static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
+{
+ float y_bottom = y_top+1;
+
+ while (e) {
+ // brute force every pixel
+
+ // compute intersection points with top & bottom
+ STBTT_assert(e->ey >= y_top);
+
+ if (e->fdx == 0) {
+ float x0 = e->fx;
+ if (x0 < len) {
+ if (x0 >= 0) {
+ stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
+ stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
+ } else {
+ stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
+ }
+ }
+ } else {
+ float x0 = e->fx;
+ float dx = e->fdx;
+ float xb = x0 + dx;
+ float x_top, x_bottom;
+ float sy0,sy1;
+ float dy = e->fdy;
+ STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
+
+ // compute endpoints of line segment clipped to this scanline (if the
+ // line segment starts on this scanline. x0 is the intersection of the
+ // line with y_top, but that may be off the line segment.
+ if (e->sy > y_top) {
+ x_top = x0 + dx * (e->sy - y_top);
+ sy0 = e->sy;
+ } else {
+ x_top = x0;
+ sy0 = y_top;
+ }
+ if (e->ey < y_bottom) {
+ x_bottom = x0 + dx * (e->ey - y_top);
+ sy1 = e->ey;
+ } else {
+ x_bottom = xb;
+ sy1 = y_bottom;
+ }
+
+ if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
+ // from here on, we don't have to range check x values
+
+ if ((int) x_top == (int) x_bottom) {
+ float height;
+ // simple case, only spans one pixel
+ int x = (int) x_top;
+ height = sy1 - sy0;
+ STBTT_assert(x >= 0 && x < len);
+ scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
+ scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
+ } else {
+ int x,x1,x2;
+ float y_crossing, step, sign, area;
+ // covers 2+ pixels
+ if (x_top > x_bottom) {
+ // flip scanline vertically; signed area is the same
+ float t;
+ sy0 = y_bottom - (sy0 - y_top);
+ sy1 = y_bottom - (sy1 - y_top);
+ t = sy0, sy0 = sy1, sy1 = t;
+ t = x_bottom, x_bottom = x_top, x_top = t;
+ dx = -dx;
+ dy = -dy;
+ t = x0, x0 = xb, xb = t;
+ }
+
+ x1 = (int) x_top;
+ x2 = (int) x_bottom;
+ // compute intersection with y axis at x1+1
+ y_crossing = (x1+1 - x0) * dy + y_top;
+
+ sign = e->direction;
+ // area of the rectangle covered from y0..y_crossing
+ area = sign * (y_crossing-sy0);
+ // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
+ scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
+
+ step = sign * dy;
+ for (x = x1+1; x < x2; ++x) {
+ scanline[x] += area + step/2;
+ area += step;
+ }
+ y_crossing += dy * (x2 - (x1+1));
+
+ STBTT_assert(STBTT_fabs(area) <= 1.01f);
+
+ scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
+
+ scanline_fill[x2] += sign * (sy1-sy0);
+ }
+ } else {
+ // if edge goes outside of box we're drawing, we require
+ // clipping logic. since this does not match the intended use
+ // of this library, we use a different, very slow brute
+ // force implementation
+ int x;
+ for (x=0; x < len; ++x) {
+ // cases:
+ //
+ // there can be up to two intersections with the pixel. any intersection
+ // with left or right edges can be handled by splitting into two (or three)
+ // regions. intersections with top & bottom do not necessitate case-wise logic.
+ //
+ // the old way of doing this found the intersections with the left & right edges,
+ // then used some simple logic to produce up to three segments in sorted order
+ // from top-to-bottom. however, this had a problem: if an x edge was epsilon
+ // across the x border, then the corresponding y position might not be distinct
+ // from the other y segment, and it might ignored as an empty segment. to avoid
+ // that, we need to explicitly produce segments based on x positions.
+
+ // rename variables to clear pairs
+ float y0 = y_top;
+ float x1 = (float) (x);
+ float x2 = (float) (x+1);
+ float x3 = xb;
+ float y3 = y_bottom;
+ float y1,y2;
+
+ // x = e->x + e->dx * (y-y_top)
+ // (y-y_top) = (x - e->x) / e->dx
+ // y = (x - e->x) / e->dx + y_top
+ y1 = (x - x0) / dx + y_top;
+ y2 = (x+1 - x0) / dx + y_top;
+
+ if (x0 < x1 && x3 > x2) { // three segments descending down-right
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else if (x3 < x1 && x0 > x2) { // three segments descending down-left
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
+ } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
+ } else { // one segment
+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
+ }
+ }
+ }
+ }
+ e = e->next;
+ }
+}
+
+// directly AA rasterize edges w/o supersampling
+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
+{
+ stbtt__hheap hh = { 0, 0, 0 };
+ stbtt__active_edge *active = NULL;
+ int y,j=0, i;
+ float scanline_data[129], *scanline, *scanline2;
+
+ STBTT__NOTUSED(vsubsample);
+
+ if (result->w > 64)
+ scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
+ else
+ scanline = scanline_data;
+
+ scanline2 = scanline + result->w;
+
+ y = off_y;
+ e[n].y0 = (float) (off_y + result->h) + 1;
+
+ while (j < result->h) {
+ // find center of pixel for this scanline
+ float scan_y_top = y + 0.0f;
+ float scan_y_bottom = y + 1.0f;
+ stbtt__active_edge **step = &active;
+
+ STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
+ STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
+
+ // update all active edges;
+ // remove all active edges that terminate before the top of this scanline
+ while (*step) {
+ stbtt__active_edge * z = *step;
+ if (z->ey <= scan_y_top) {
+ *step = z->next; // delete from list
+ STBTT_assert(z->direction);
+ z->direction = 0;
+ stbtt__hheap_free(&hh, z);
+ } else {
+ step = &((*step)->next); // advance through list
+ }
+ }
+
+ // insert all edges that start before the bottom of this scanline
+ while (e->y0 <= scan_y_bottom) {
+ if (e->y0 != e->y1) {
+ stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
+ if (z != NULL) {
+ STBTT_assert(z->ey >= scan_y_top);
+ // insert at front
+ z->next = active;
+ active = z;
+ }
+ }
+ ++e;
+ }
+
+ // now process all active edges
+ if (active)
+ stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
+
+ {
+ float sum = 0;
+ for (i=0; i < result->w; ++i) {
+ float k;
+ int m;
+ sum += scanline2[i];
+ k = scanline[i] + sum;
+ k = (float) STBTT_fabs(k)*255 + 0.5f;
+ m = (int) k;
+ if (m > 255) m = 255;
+ result->pixels[j*result->stride + i] = (unsigned char) m;
+ }
+ }
+ // advance all the edges
+ step = &active;
+ while (*step) {
+ stbtt__active_edge *z = *step;
+ z->fx += z->fdx; // advance to position for current scanline
+ step = &((*step)->next); // advance through list
+ }
+
+ ++y;
+ ++j;
+ }
+
+ stbtt__hheap_cleanup(&hh, userdata);
+
+ if (scanline != scanline_data)
+ STBTT_free(scanline, userdata);
+}
+#else
+#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+#endif
+
+#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0)
+
+static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
+{
+ int i,j;
+ for (i=1; i < n; ++i) {
+ stbtt__edge t = p[i], *a = &t;
+ j = i;
+ while (j > 0) {
+ stbtt__edge *b = &p[j-1];
+ int c = STBTT__COMPARE(a,b);
+ if (!c) break;
+ p[j] = p[j-1];
+ --j;
+ }
+ if (i != j)
+ p[j] = t;
+ }
+}
+
+static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
+{
+ /* threshhold for transitioning to insertion sort */
+ while (n > 12) {
+ stbtt__edge t;
+ int c01,c12,c,m,i,j;
+
+ /* compute median of three */
+ m = n >> 1;
+ c01 = STBTT__COMPARE(&p[0],&p[m]);
+ c12 = STBTT__COMPARE(&p[m],&p[n-1]);
+ /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
+ if (c01 != c12) {
+ /* otherwise, we'll need to swap something else to middle */
+ int z;
+ c = STBTT__COMPARE(&p[0],&p[n-1]);
+ /* 0>mid && mid<n: 0>n => n; 0<n => 0 */
+ /* 0<mid && mid>n: 0>n => 0; 0<n => n */
+ z = (c == c12) ? 0 : n-1;
+ t = p[z];
+ p[z] = p[m];
+ p[m] = t;
+ }
+ /* now p[m] is the median-of-three */
+ /* swap it to the beginning so it won't move around */
+ t = p[0];
+ p[0] = p[m];
+ p[m] = t;
+
+ /* partition loop */
+ i=1;
+ j=n-1;
+ for(;;) {
+ /* handling of equality is crucial here */
+ /* for sentinels & efficiency with duplicates */
+ for (;;++i) {
+ if (!STBTT__COMPARE(&p[i], &p[0])) break;
+ }
+ for (;;--j) {
+ if (!STBTT__COMPARE(&p[0], &p[j])) break;
+ }
+ /* make sure we haven't crossed */
+ if (i >= j) break;
+ t = p[i];
+ p[i] = p[j];
+ p[j] = t;
+
+ ++i;
+ --j;
+ }
+ /* recurse on smaller side, iterate on larger */
+ if (j < (n-i)) {
+ stbtt__sort_edges_quicksort(p,j);
+ p = p+i;
+ n = n-i;
+ } else {
+ stbtt__sort_edges_quicksort(p+i, n-i);
+ n = j;
+ }
+ }
+}
+
+static void stbtt__sort_edges(stbtt__edge *p, int n)
+{
+ stbtt__sort_edges_quicksort(p, n);
+ stbtt__sort_edges_ins_sort(p, n);
+}
+
+typedef struct
+{
+ float x,y;
+} stbtt__point;
+
+static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
+{
+ float y_scale_inv = invert ? -scale_y : scale_y;
+ stbtt__edge *e;
+ int n,i,j,k,m;
+#if STBTT_RASTERIZER_VERSION == 1
+ int vsubsample = result->h < 8 ? 15 : 5;
+#elif STBTT_RASTERIZER_VERSION == 2
+ int vsubsample = 1;
+#else
+ #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
+#endif
+ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
+
+ // now we have to blow out the windings into explicit edge lists
+ n = 0;
+ for (i=0; i < windings; ++i)
+ n += wcount[i];
+
+ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
+ if (e == 0) return;
+ n = 0;
+
+ m=0;
+ for (i=0; i < windings; ++i) {
+ stbtt__point *p = pts + m;
+ m += wcount[i];
+ j = wcount[i]-1;
+ for (k=0; k < wcount[i]; j=k++) {
+ int a=k,b=j;
+ // skip the edge if horizontal
+ if (p[j].y == p[k].y)
+ continue;
+ // add edge from j to k to the list
+ e[n].invert = 0;
+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
+ e[n].invert = 1;
+ a=j,b=k;
+ }
+ e[n].x0 = p[a].x * scale_x + shift_x;
+ e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
+ e[n].x1 = p[b].x * scale_x + shift_x;
+ e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
+ ++n;
+ }
+ }
+
+ // now sort the edges by their highest point (should snap to integer, and then by x)
+ //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
+ stbtt__sort_edges(e, n);
+
+ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
+ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
+
+ STBTT_free(e, userdata);
+}
+
+static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
+{
+ if (!points) return; // during first pass, it's unallocated
+ points[n].x = x;
+ points[n].y = y;
+}
+
+// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
+static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
+{
+ // midpoint
+ float mx = (x0 + 2*x1 + x2)/4;
+ float my = (y0 + 2*y1 + y2)/4;
+ // versus directly drawn line
+ float dx = (x0+x2)/2 - mx;
+ float dy = (y0+y2)/2 - my;
+ if (n > 16) // 65536 segments on one curve better be enough!
+ return 1;
+ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
+ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
+ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
+ } else {
+ stbtt__add_point(points, *num_points,x2,y2);
+ *num_points = *num_points+1;
+ }
+ return 1;
+}
+
+// returns number of contours
+static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
+{
+ stbtt__point *points=0;
+ int num_points=0;
+
+ float objspace_flatness_squared = objspace_flatness * objspace_flatness;
+ int i,n=0,start=0, pass;
+
+ // count how many "moves" there are to get the contour count
+ for (i=0; i < num_verts; ++i)
+ if (vertices[i].type == STBTT_vmove)
+ ++n;
+
+ *num_contours = n;
+ if (n == 0) return 0;
+
+ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
+
+ if (*contour_lengths == 0) {
+ *num_contours = 0;
+ return 0;
+ }
+
+ // make two passes through the points so we don't need to realloc
+ for (pass=0; pass < 2; ++pass) {
+ float x=0,y=0;
+ if (pass == 1) {
+ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
+ if (points == NULL) goto error;
+ }
+ num_points = 0;
+ n= -1;
+ for (i=0; i < num_verts; ++i) {
+ switch (vertices[i].type) {
+ case STBTT_vmove:
+ // start the next contour
+ if (n >= 0)
+ (*contour_lengths)[n] = num_points - start;
+ ++n;
+ start = num_points;
+
+ x = vertices[i].x, y = vertices[i].y;
+ stbtt__add_point(points, num_points++, x,y);
+ break;
+ case STBTT_vline:
+ x = vertices[i].x, y = vertices[i].y;
+ stbtt__add_point(points, num_points++, x, y);
+ break;
+ case STBTT_vcurve:
+ stbtt__tesselate_curve(points, &num_points, x,y,
+ vertices[i].cx, vertices[i].cy,
+ vertices[i].x, vertices[i].y,
+ objspace_flatness_squared, 0);
+ x = vertices[i].x, y = vertices[i].y;
+ break;
+ }
+ }
+ (*contour_lengths)[n] = num_points - start;
+ }
+
+ return points;
+error:
+ STBTT_free(points, userdata);
+ STBTT_free(*contour_lengths, userdata);
+ *contour_lengths = 0;
+ *num_contours = 0;
+ return NULL;
+}
+
+STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
+{
+ float scale = scale_x > scale_y ? scale_y : scale_x;
+ int winding_count, *winding_lengths;
+ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
+ if (windings) {
+ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
+ STBTT_free(winding_lengths, userdata);
+ STBTT_free(windings, userdata);
+ }
+}
+
+STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
+{
+ STBTT_free(bitmap, userdata);
+}
+
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
+{
+ int ix0,iy0,ix1,iy1;
+ stbtt__bitmap gbm;
+ stbtt_vertex *vertices;
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
+
+ if (scale_x == 0) scale_x = scale_y;
+ if (scale_y == 0) {
+ if (scale_x == 0) {
+ STBTT_free(vertices, info->userdata);
+ return NULL;
+ }
+ scale_y = scale_x;
+ }
+
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
+
+ // now we get the size
+ gbm.w = (ix1 - ix0);
+ gbm.h = (iy1 - iy0);
+ gbm.pixels = NULL; // in case we error
+
+ if (width ) *width = gbm.w;
+ if (height) *height = gbm.h;
+ if (xoff ) *xoff = ix0;
+ if (yoff ) *yoff = iy0;
+
+ if (gbm.w && gbm.h) {
+ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
+ if (gbm.pixels) {
+ gbm.stride = gbm.w;
+
+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
+ }
+ }
+ STBTT_free(vertices, info->userdata);
+ return gbm.pixels;
+}
+
+STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
+}
+
+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
+{
+ int ix0,iy0;
+ stbtt_vertex *vertices;
+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
+ stbtt__bitmap gbm;
+
+ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
+ gbm.pixels = output;
+ gbm.w = out_w;
+ gbm.h = out_h;
+ gbm.stride = out_stride;
+
+ if (gbm.w && gbm.h)
+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
+
+ STBTT_free(vertices, info->userdata);
+}
+
+STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
+}
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
+}
+
+STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
+{
+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
+}
+
+STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
+{
+ return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
+}
+
+STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
+{
+ stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bitmap baking
+//
+// This is SUPER-CRAPPY packing to keep source code small
+
+STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
+ float pixel_height, // height of font in pixels
+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
+ int first_char, int num_chars, // characters to bake
+ stbtt_bakedchar *chardata)
+{
+ float scale;
+ int x,y,bottom_y, i;
+ stbtt_fontinfo f;
+ f.userdata = NULL;
+ if (!stbtt_InitFont(&f, data, offset))
+ return -1;
+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
+ x=y=1;
+ bottom_y = 1;
+
+ scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
+
+ for (i=0; i < num_chars; ++i) {
+ int advance, lsb, x0,y0,x1,y1,gw,gh;
+ int g = stbtt_FindGlyphIndex(&f, first_char + i);
+ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
+ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
+ gw = x1-x0;
+ gh = y1-y0;
+ if (x + gw + 1 >= pw)
+ y = bottom_y, x = 1; // advance to next row
+ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
+ return -i;
+ STBTT_assert(x+gw < pw);
+ STBTT_assert(y+gh < ph);
+ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
+ chardata[i].x0 = (stbtt_int16) x;
+ chardata[i].y0 = (stbtt_int16) y;
+ chardata[i].x1 = (stbtt_int16) (x + gw);
+ chardata[i].y1 = (stbtt_int16) (y + gh);
+ chardata[i].xadvance = scale * advance;
+ chardata[i].xoff = (float) x0;
+ chardata[i].yoff = (float) y0;
+ x = x + gw + 1;
+ if (y+gh+1 > bottom_y)
+ bottom_y = y+gh+1;
+ }
+ return bottom_y;
+}
+
+STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
+{
+ float d3d_bias = opengl_fillrule ? 0 : -0.5f;
+ float ipw = 1.0f / pw, iph = 1.0f / ph;
+ stbtt_bakedchar *b = chardata + char_index;
+ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
+ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
+
+ q->x0 = round_x + d3d_bias;
+ q->y0 = round_y + d3d_bias;
+ q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
+ q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
+
+ q->s0 = b->x0 * ipw;
+ q->t0 = b->y0 * iph;
+ q->s1 = b->x1 * ipw;
+ q->t1 = b->y1 * iph;
+
+ *xpos += b->xadvance;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// rectangle packing replacement routines if you don't have stb_rect_pack.h
+//
+
+#ifndef STB_RECT_PACK_VERSION
+
+typedef int stbrp_coord;
+
+////////////////////////////////////////////////////////////////////////////////////
+// //
+// //
+// COMPILER WARNING ?!?!? //
+// //
+// //
+// if you get a compile warning due to these symbols being defined more than //
+// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" //
+// //
+////////////////////////////////////////////////////////////////////////////////////
+
+typedef struct
+{
+ int width,height;
+ int x,y,bottom_y;
+} stbrp_context;
+
+typedef struct
+{
+ unsigned char x;
+} stbrp_node;
+
+struct stbrp_rect
+{
+ stbrp_coord x,y;
+ int id,w,h,was_packed;
+};
+
+static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
+{
+ con->width = pw;
+ con->height = ph;
+ con->x = 0;
+ con->y = 0;
+ con->bottom_y = 0;
+ STBTT__NOTUSED(nodes);
+ STBTT__NOTUSED(num_nodes);
+}
+
+static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
+{
+ int i;
+ for (i=0; i < num_rects; ++i) {
+ if (con->x + rects[i].w > con->width) {
+ con->x = 0;
+ con->y = con->bottom_y;
+ }
+ if (con->y + rects[i].h > con->height)
+ break;
+ rects[i].x = con->x;
+ rects[i].y = con->y;
+ rects[i].was_packed = 1;
+ con->x += rects[i].w;
+ if (con->y + rects[i].h > con->bottom_y)
+ con->bottom_y = con->y + rects[i].h;
+ }
+ for ( ; i < num_rects; ++i)
+ rects[i].was_packed = 0;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bitmap baking
+//
+// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
+// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
+
+STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
+{
+ stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
+ int num_nodes = pw - padding;
+ stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);
+
+ if (context == NULL || nodes == NULL) {
+ if (context != NULL) STBTT_free(context, alloc_context);
+ if (nodes != NULL) STBTT_free(nodes , alloc_context);
+ return 0;
+ }
+
+ spc->user_allocator_context = alloc_context;
+ spc->width = pw;
+ spc->height = ph;
+ spc->pixels = pixels;
+ spc->pack_info = context;
+ spc->nodes = nodes;
+ spc->padding = padding;
+ spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
+ spc->h_oversample = 1;
+ spc->v_oversample = 1;
+
+ stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
+
+ if (pixels)
+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
+
+ return 1;
+}
+
+STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)
+{
+ STBTT_free(spc->nodes , spc->user_allocator_context);
+ STBTT_free(spc->pack_info, spc->user_allocator_context);
+}
+
+STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
+{
+ STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
+ STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
+ if (h_oversample <= STBTT_MAX_OVERSAMPLE)
+ spc->h_oversample = h_oversample;
+ if (v_oversample <= STBTT_MAX_OVERSAMPLE)
+ spc->v_oversample = v_oversample;
+}
+
+#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
+
+static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
+{
+ unsigned char buffer[STBTT_MAX_OVERSAMPLE];
+ int safe_w = w - kernel_width;
+ int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
+ for (j=0; j < h; ++j) {
+ int i;
+ unsigned int total;
+ STBTT_memset(buffer, 0, kernel_width);
+
+ total = 0;
+
+ // make kernel_width a constant in common cases so compiler can optimize out the divide
+ switch (kernel_width) {
+ case 2:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 2);
+ }
+ break;
+ case 3:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 3);
+ }
+ break;
+ case 4:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 4);
+ }
+ break;
+ case 5:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / 5);
+ }
+ break;
+ default:
+ for (i=0; i <= safe_w; ++i) {
+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
+ pixels[i] = (unsigned char) (total / kernel_width);
+ }
+ break;
+ }
+
+ for (; i < w; ++i) {
+ STBTT_assert(pixels[i] == 0);
+ total -= buffer[i & STBTT__OVER_MASK];
+ pixels[i] = (unsigned char) (total / kernel_width);
+ }
+
+ pixels += stride_in_bytes;
+ }
+}
+
+static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
+{
+ unsigned char buffer[STBTT_MAX_OVERSAMPLE];
+ int safe_h = h - kernel_width;
+ int j;
+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
+ for (j=0; j < w; ++j) {
+ int i;
+ unsigned int total;
+ STBTT_memset(buffer, 0, kernel_width);
+
+ total = 0;
+
+ // make kernel_width a constant in common cases so compiler can optimize out the divide
+ switch (kernel_width) {
+ case 2:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
+ }
+ break;
+ case 3:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
+ }
+ break;
+ case 4:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
+ }
+ break;
+ case 5:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
+ }
+ break;
+ default:
+ for (i=0; i <= safe_h; ++i) {
+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
+ }
+ break;
+ }
+
+ for (; i < h; ++i) {
+ STBTT_assert(pixels[i*stride_in_bytes] == 0);
+ total -= buffer[i & STBTT__OVER_MASK];
+ pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
+ }
+
+ pixels += 1;
+ }
+}
+
+static float stbtt__oversample_shift(int oversample)
+{
+ if (!oversample)
+ return 0.0f;
+
+ // The prefilter is a box filter of width "oversample",
+ // which shifts phase by (oversample - 1)/2 pixels in
+ // oversampled space. We want to shift in the opposite
+ // direction to counter this.
+ return (float)-(oversample - 1) / (2.0f * (float)oversample);
+}
+
+// rects array must be big enough to accommodate all characters in the given ranges
+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
+{
+ int i,j,k;
+
+ k=0;
+ for (i=0; i < num_ranges; ++i) {
+ float fh = ranges[i].font_size;
+ float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
+ ranges[i].h_oversample = (unsigned char) spc->h_oversample;
+ ranges[i].v_oversample = (unsigned char) spc->v_oversample;
+ for (j=0; j < ranges[i].num_chars; ++j) {
+ int x0,y0,x1,y1;
+ int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
+ int glyph = stbtt_FindGlyphIndex(info, codepoint);
+ stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ 0,0,
+ &x0,&y0,&x1,&y1);
+ rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
+ rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
+ ++k;
+ }
+ }
+
+ return k;
+}
+
+// rects array must be big enough to accommodate all characters in the given ranges
+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
+{
+ int i,j,k, return_value = 1;
+
+ // save current values
+ int old_h_over = spc->h_oversample;
+ int old_v_over = spc->v_oversample;
+
+ k = 0;
+ for (i=0; i < num_ranges; ++i) {
+ float fh = ranges[i].font_size;
+ float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
+ float recip_h,recip_v,sub_x,sub_y;
+ spc->h_oversample = ranges[i].h_oversample;
+ spc->v_oversample = ranges[i].v_oversample;
+ recip_h = 1.0f / spc->h_oversample;
+ recip_v = 1.0f / spc->v_oversample;
+ sub_x = stbtt__oversample_shift(spc->h_oversample);
+ sub_y = stbtt__oversample_shift(spc->v_oversample);
+ for (j=0; j < ranges[i].num_chars; ++j) {
+ stbrp_rect *r = &rects[k];
+ if (r->was_packed) {
+ stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
+ int advance, lsb, x0,y0,x1,y1;
+ int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
+ int glyph = stbtt_FindGlyphIndex(info, codepoint);
+ stbrp_coord pad = (stbrp_coord) spc->padding;
+
+ // pad on left and top
+ r->x += pad;
+ r->y += pad;
+ r->w -= pad;
+ r->h -= pad;
+ stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
+ stbtt_GetGlyphBitmapBox(info, glyph,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ &x0,&y0,&x1,&y1);
+ stbtt_MakeGlyphBitmapSubpixel(info,
+ spc->pixels + r->x + r->y*spc->stride_in_bytes,
+ r->w - spc->h_oversample+1,
+ r->h - spc->v_oversample+1,
+ spc->stride_in_bytes,
+ scale * spc->h_oversample,
+ scale * spc->v_oversample,
+ 0,0,
+ glyph);
+
+ if (spc->h_oversample > 1)
+ stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+ r->w, r->h, spc->stride_in_bytes,
+ spc->h_oversample);
+
+ if (spc->v_oversample > 1)
+ stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
+ r->w, r->h, spc->stride_in_bytes,
+ spc->v_oversample);
+
+ bc->x0 = (stbtt_int16) r->x;
+ bc->y0 = (stbtt_int16) r->y;
+ bc->x1 = (stbtt_int16) (r->x + r->w);
+ bc->y1 = (stbtt_int16) (r->y + r->h);
+ bc->xadvance = scale * advance;
+ bc->xoff = (float) x0 * recip_h + sub_x;
+ bc->yoff = (float) y0 * recip_v + sub_y;
+ bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
+ bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
+ } else {
+ return_value = 0; // if any fail, report failure
+ }
+
+ ++k;
+ }
+ }
+
+ // restore original values
+ spc->h_oversample = old_h_over;
+ spc->v_oversample = old_v_over;
+
+ return return_value;
+}
+
+STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
+{
+ stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
+}
+
+STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
+{
+ stbtt_fontinfo info;
+ int i,j,n, return_value = 1;
+ //stbrp_context *context = (stbrp_context *) spc->pack_info;
+ stbrp_rect *rects;
+
+ // flag all characters as NOT packed
+ for (i=0; i < num_ranges; ++i)
+ for (j=0; j < ranges[i].num_chars; ++j)
+ ranges[i].chardata_for_range[j].x0 =
+ ranges[i].chardata_for_range[j].y0 =
+ ranges[i].chardata_for_range[j].x1 =
+ ranges[i].chardata_for_range[j].y1 = 0;
+
+ n = 0;
+ for (i=0; i < num_ranges; ++i)
+ n += ranges[i].num_chars;
+
+ rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
+ if (rects == NULL)
+ return 0;
+
+ info.userdata = spc->user_allocator_context;
+ stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
+
+ n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
+
+ stbtt_PackFontRangesPackRects(spc, rects, n);
+
+ return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
+
+ STBTT_free(rects, spc->user_allocator_context);
+ return return_value;
+}
+
+STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
+ int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
+{
+ stbtt_pack_range range;
+ range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
+ range.array_of_unicode_codepoints = NULL;
+ range.num_chars = num_chars_in_range;
+ range.chardata_for_range = chardata_for_range;
+ range.font_size = font_size;
+ return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
+}
+
+STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
+{
+ float ipw = 1.0f / pw, iph = 1.0f / ph;
+ stbtt_packedchar *b = chardata + char_index;
+
+ if (align_to_integer) {
+ float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
+ float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
+ q->x0 = x;
+ q->y0 = y;
+ q->x1 = x + b->xoff2 - b->xoff;
+ q->y1 = y + b->yoff2 - b->yoff;
+ } else {
+ q->x0 = *xpos + b->xoff;
+ q->y0 = *ypos + b->yoff;
+ q->x1 = *xpos + b->xoff2;
+ q->y1 = *ypos + b->yoff2;
+ }
+
+ q->s0 = b->x0 * ipw;
+ q->t0 = b->y0 * iph;
+ q->s1 = b->x1 * ipw;
+ q->t1 = b->y1 * iph;
+
+ *xpos += b->xadvance;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// font name matching -- recommended not to use this
+//
+
+// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2)
+{
+ stbtt_int32 i=0;
+
+ // convert utf16 to utf8 and compare the results while converting
+ while (len2) {
+ stbtt_uint16 ch = s2[0]*256 + s2[1];
+ if (ch < 0x80) {
+ if (i >= len1) return -1;
+ if (s1[i++] != ch) return -1;
+ } else if (ch < 0x800) {
+ if (i+1 >= len1) return -1;
+ if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
+ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
+ } else if (ch >= 0xd800 && ch < 0xdc00) {
+ stbtt_uint32 c;
+ stbtt_uint16 ch2 = s2[2]*256 + s2[3];
+ if (i+3 >= len1) return -1;
+ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
+ if (s1[i++] != 0xf0 + (c >> 18)) return -1;
+ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
+ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
+ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
+ s2 += 2; // plus another 2 below
+ len2 -= 2;
+ } else if (ch >= 0xdc00 && ch < 0xe000) {
+ return -1;
+ } else {
+ if (i+2 >= len1) return -1;
+ if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
+ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
+ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
+ }
+ s2 += 2;
+ len2 -= 2;
+ }
+ return i;
+}
+
+STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
+{
+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2);
+}
+
+// returns results in whatever encoding you request... but note that 2-byte encodings
+// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
+STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
+{
+ stbtt_int32 i,count,stringOffset;
+ stbtt_uint8 *fc = font->data;
+ stbtt_uint32 offset = font->fontstart;
+ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
+ if (!nm) return NULL;
+
+ count = ttUSHORT(fc+nm+2);
+ stringOffset = nm + ttUSHORT(fc+nm+4);
+ for (i=0; i < count; ++i) {
+ stbtt_uint32 loc = nm + 6 + 12 * i;
+ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
+ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
+ *length = ttUSHORT(fc+loc+8);
+ return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
+ }
+ }
+ return NULL;
+}
+
+static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
+{
+ stbtt_int32 i;
+ stbtt_int32 count = ttUSHORT(fc+nm+2);
+ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
+
+ for (i=0; i < count; ++i) {
+ stbtt_uint32 loc = nm + 6 + 12 * i;
+ stbtt_int32 id = ttUSHORT(fc+loc+6);
+ if (id == target_id) {
+ // find the encoding
+ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
+
+ // is this a Unicode encoding?
+ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
+ stbtt_int32 slen = ttUSHORT(fc+loc+8);
+ stbtt_int32 off = ttUSHORT(fc+loc+10);
+
+ // check if there's a prefix match
+ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
+ if (matchlen >= 0) {
+ // check for target_id+1 immediately following, with same encoding & language
+ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
+ slen = ttUSHORT(fc+loc+12+8);
+ off = ttUSHORT(fc+loc+12+10);
+ if (slen == 0) {
+ if (matchlen == nlen)
+ return 1;
+ } else if (matchlen < nlen && name[matchlen] == ' ') {
+ ++matchlen;
+ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
+ return 1;
+ }
+ } else {
+ // if nothing immediately following
+ if (matchlen == nlen)
+ return 1;
+ }
+ }
+ }
+
+ // @TODO handle other encodings
+ }
+ }
+ return 0;
+}
+
+static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
+{
+ stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
+ stbtt_uint32 nm,hd;
+ if (!stbtt__isfont(fc+offset)) return 0;
+
+ // check italics/bold/underline flags in macStyle...
+ if (flags) {
+ hd = stbtt__find_table(fc, offset, "head");
+ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
+ }
+
+ nm = stbtt__find_table(fc, offset, "name");
+ if (!nm) return 0;
+
+ if (flags) {
+ // if we checked the macStyle flags, then just check the family and ignore the subfamily
+ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
+ } else {
+ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
+ }
+
+ return 0;
+}
+
+STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
+{
+ stbtt_int32 i;
+ for (i=0;;++i) {
+ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
+ if (off < 0) return off;
+ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
+ return off;
+ }
+}
+
+#endif // STB_TRUETYPE_IMPLEMENTATION
+
+
+// FULL VERSION HISTORY
+//
+// 1.11 (2016-04-02) fix unused-variable warning
+// 1.10 (2016-04-02) allow user-defined fabs() replacement
+// fix memory leak if fontsize=0.0
+// fix warning from duplicate typedef
+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
+// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
+// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
+// allow PackFontRanges to pack and render in separate phases;
+// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
+// fixed an assert() bug in the new rasterizer
+// replace assert() with STBTT_assert() in new rasterizer
+// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
+// also more precise AA rasterizer, except if shapes overlap
+// remove need for STBTT_sort
+// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
+// 1.04 (2015-04-15) typo in example
+// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
+// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
+// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
+// non-oversampled; STBTT_POINT_SIZE for packed case only
+// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
+// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
+// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID
+// 0.8b (2014-07-07) fix a warning
+// 0.8 (2014-05-25) fix a few more warnings
+// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
+// 0.6c (2012-07-24) improve documentation
+// 0.6b (2012-07-20) fix a few more warnings
+// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
+// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
+// 0.5 (2011-12-09) bugfixes:
+// subpixel glyph renderer computed wrong bounding box
+// first vertex of shape can be off-curve (FreeSans)
+// 0.4b (2011-12-03) fixed an error in the font baking example
+// 0.4 (2011-12-01) kerning, subpixel rendering (tor)
+// bugfixes for:
+// codepoint-to-glyph conversion using table fmt=12
+// codepoint-to-glyph conversion using table fmt=4
+// stbtt_GetBakedQuad with non-square texture (Zer)
+// updated Hello World! sample to use kerning and subpixel
+// fixed some warnings
+// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
+// userdata, malloc-from-userdata, non-zero fill (stb)
+// 0.2 (2009-03-11) Fix unsigned/signed char warnings
+// 0.1 (2009-03-09) First public release
+//
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index 75e38f9701..a61ffe8e97 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -138,7 +138,7 @@ void StyleBoxTexture::draw(RID p_canvas_item,const Rect2& p_rect) const {
r.pos.y-=expand_margin[MARGIN_TOP];
r.size.x+=expand_margin[MARGIN_LEFT]+expand_margin[MARGIN_RIGHT];
r.size.y+=expand_margin[MARGIN_TOP]+expand_margin[MARGIN_BOTTOM];
- VisualServer::get_singleton()->canvas_item_add_style_box( p_canvas_item,r,texture->get_rid(),Vector2(margin[MARGIN_LEFT],margin[MARGIN_TOP]),Vector2(margin[MARGIN_RIGHT],margin[MARGIN_BOTTOM]),draw_center);
+ VisualServer::get_singleton()->canvas_item_add_style_box( p_canvas_item,r,region_rect,texture->get_rid(),Vector2(margin[MARGIN_LEFT],margin[MARGIN_TOP]),Vector2(margin[MARGIN_RIGHT],margin[MARGIN_BOTTOM]),draw_center);
}
void StyleBoxTexture::set_draw_center(bool p_draw) {
@@ -175,6 +175,20 @@ float StyleBoxTexture::get_expand_margin_size(Margin p_expand_margin) const {
return expand_margin[p_expand_margin];
}
+void StyleBoxTexture::set_region_rect(const Rect2& p_region_rect) {
+
+ if (region_rect==p_region_rect)
+ return;
+
+ region_rect=p_region_rect;
+ emit_changed();
+}
+
+Rect2 StyleBoxTexture::get_region_rect() const {
+
+ return region_rect;
+}
+
void StyleBoxTexture::_bind_methods() {
@@ -187,10 +201,14 @@ void StyleBoxTexture::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_expand_margin_size","margin","size"),&StyleBoxTexture::set_expand_margin_size);
ObjectTypeDB::bind_method(_MD("get_expand_margin_size","margin"),&StyleBoxTexture::get_expand_margin_size);
+ ObjectTypeDB::bind_method(_MD("set_region_rect","region"),&StyleBoxTexture::set_region_rect);
+ ObjectTypeDB::bind_method(_MD("get_region_rect"),&StyleBoxTexture::get_region_rect);
+
ObjectTypeDB::bind_method(_MD("set_draw_center","enable"),&StyleBoxTexture::set_draw_center);
ObjectTypeDB::bind_method(_MD("get_draw_center"),&StyleBoxTexture::get_draw_center);
ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), _SCS("set_texture"),_SCS("get_texture") );
+ ADD_PROPERTYNZ( PropertyInfo( Variant::RECT2, "region_rect"), _SCS("set_region_rect"),_SCS("get_region_rect"));
ADD_PROPERTYI( PropertyInfo( Variant::REAL, "margin/left", PROPERTY_HINT_RANGE,"0,2048,1" ), _SCS("set_margin_size"),_SCS("get_margin_size"), MARGIN_LEFT );
ADD_PROPERTYI( PropertyInfo( Variant::REAL, "margin/right", PROPERTY_HINT_RANGE,"0,2048,1" ), _SCS("set_margin_size"),_SCS("get_margin_size"), MARGIN_RIGHT );
ADD_PROPERTYI( PropertyInfo( Variant::REAL, "margin/top", PROPERTY_HINT_RANGE,"0,2048,1" ), _SCS("set_margin_size"),_SCS("get_margin_size"), MARGIN_TOP);
@@ -505,4 +523,3 @@ StyleBoxImageMask::StyleBoxImageMask() {
}
expand=true;
}
-
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 02d79bc2ac..98aaee754b 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -81,6 +81,7 @@ class StyleBoxTexture : public StyleBox {
float expand_margin[4];
float margin[4];
+ Rect2 region_rect;
Ref<Texture> texture;
bool draw_center;
@@ -98,6 +99,9 @@ public:
void set_margin_size(Margin p_margin,float p_size);
float get_margin_size(Margin p_margin) const;
+ void set_region_rect(const Rect2& p_region_rect);
+ Rect2 get_region_rect() const;
+
void set_texture(RES p_texture);
RES get_texture() const;
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index e3a02420c7..12382e6678 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -27,6 +27,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "surface_tool.h"
+#include "method_bind_ext.inc"
#define _VERTEX_SNAP 0.0001
#define EQ_VERTEX_DIST 0.00001
@@ -196,6 +197,35 @@ void SurfaceTool::add_smooth_group(bool p_smooth) {
}
}
+void SurfaceTool::add_triangle_fan(const Vector<Vector3>& p_vertexes, const Vector<Vector2>& p_uvs, const Vector<Color>& p_colors,const Vector<Vector2>& p_uv2s, const Vector<Vector3>& p_normals, const Vector<Plane>& p_tangents) {
+ ERR_FAIL_COND(!begun);
+ ERR_FAIL_COND(primitive!=Mesh::PRIMITIVE_TRIANGLES);
+ ERR_FAIL_COND(p_vertexes.size()<3);
+
+#define ADD_POINT(n)\
+ {\
+ if(p_colors.size() > n)\
+ add_color(p_colors[n]);\
+ if(p_uvs.size() > n)\
+ add_uv(p_uvs[n]);\
+ if(p_uv2s.size() > n)\
+ add_uv2(p_uv2s[n]);\
+ if(p_normals.size() > n)\
+ add_normal(p_normals[n]);\
+ if(p_tangents.size() > n)\
+ add_tangent(p_tangents[n]);\
+ add_vertex(p_vertexes[n]);\
+ }
+
+ for(int i=0;i<p_vertexes.size() - 2;i++) {
+ ADD_POINT(0);
+ ADD_POINT(i+1);
+ ADD_POINT(i+2);
+ }
+
+#undef ADD_POINT
+
+}
void SurfaceTool::add_index( int p_index) {
@@ -826,12 +856,13 @@ void SurfaceTool::_bind_methods() {
ObjectTypeDB::bind_method(_MD("add_bones","bones"),&SurfaceTool::add_bones);
ObjectTypeDB::bind_method(_MD("add_weights","weights"),&SurfaceTool::add_weights);
ObjectTypeDB::bind_method(_MD("add_smooth_group","smooth"),&SurfaceTool::add_smooth_group);
+ ObjectTypeDB::bind_method(_MD("add_triangle_fan", "vertexes", "uvs", "colors", "uv2s", "normals", "tangents"),&SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()),DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>()));
ObjectTypeDB::bind_method(_MD("set_material","material:Material"),&SurfaceTool::set_material);
ObjectTypeDB::bind_method(_MD("index"),&SurfaceTool::index);
ObjectTypeDB::bind_method(_MD("deindex"),&SurfaceTool::deindex);
///ObjectTypeDB::bind_method(_MD("generate_flat_normals"),&SurfaceTool::generate_flat_normals);
ObjectTypeDB::bind_method(_MD("generate_normals"),&SurfaceTool::generate_normals);
- ObjectTypeDB::bind_method(_MD("commit:Mesh","existing:Mesh"),&SurfaceTool::commit,DEFVAL( RefPtr() ));
+ ObjectTypeDB::bind_method(_MD("commit:Mesh","existing:Mesh"),&SurfaceTool::commit,DEFVAL(Variant()));
ObjectTypeDB::bind_method(_MD("clear"),&SurfaceTool::clear);
}
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 1d329f23c0..fa9724b142 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -108,6 +108,8 @@ public:
void add_weights( const Vector<float>& p_weights);
void add_smooth_group(bool p_smooth);
+ void add_triangle_fan( const Vector<Vector3>& p_vertexes, const Vector<Vector2>& p_uvs=Vector<Vector2>(), const Vector<Color>& p_colors=Vector<Color>(), const Vector<Vector2>& p_uv2s=Vector<Vector2>(), const Vector<Vector3>& p_normals=Vector<Vector3>(), const Vector<Plane>& p_tangents=Vector<Plane>() );
+
void add_index( int p_index);
void index();
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 74e59b09c3..8d0aedbf93 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -370,6 +370,13 @@ void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const
}
+void Theme::get_stylebox_types(List<StringName> *p_list) const {
+ const StringName *key=NULL;
+ while((key=style_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
void Theme::set_font(const StringName& p_name,const StringName& p_type,const Ref<Font>& p_font) {
ERR_FAIL_COND(p_font.is_null());
@@ -380,7 +387,6 @@ void Theme::set_font(const StringName& p_name,const StringName& p_type,const Ref
if (new_value) {
_change_notify();
emit_changed();;
-
}
}
Ref<Font> Theme::get_font(const StringName& p_name,const StringName& p_type) const {
@@ -604,6 +610,7 @@ void Theme::_bind_methods() {
ObjectTypeDB::bind_method(_MD("has_stylebox","name","type"),&Theme::has_stylebox);
ObjectTypeDB::bind_method(_MD("clear_stylebox","name","type"),&Theme::clear_stylebox);
ObjectTypeDB::bind_method(_MD("get_stylebox_list","type"),&Theme::_get_stylebox_list);
+ ObjectTypeDB::bind_method(_MD("get_stylebox_types"),&Theme::_get_stylebox_types);
ObjectTypeDB::bind_method(_MD("set_font","name","type","font:Font"),&Theme::set_font);
ObjectTypeDB::bind_method(_MD("get_font:Font","name","type"),&Theme::get_font);
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 36630087f6..9b84d0e8ad 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -65,6 +65,7 @@ protected:
DVector<String> _get_icon_list(const String& p_type) const { DVector<String> ilret; List<StringName> il; get_icon_list(p_type,&il); for(List<StringName>::Element *E=il.front();E;E=E->next()) { ilret.push_back(E->get()); } return ilret; }
DVector<String> _get_stylebox_list(const String& p_type) const { DVector<String> ilret; List<StringName> il; get_stylebox_list(p_type,&il); for(List<StringName>::Element *E=il.front();E;E=E->next()) { ilret.push_back(E->get()); } return ilret; }
+ DVector<String> _get_stylebox_types(void) const { DVector<String> ilret; List<StringName> il; get_stylebox_types(&il); for(List<StringName>::Element *E=il.front();E;E=E->next()) { ilret.push_back(E->get()); } return ilret; }
DVector<String> _get_font_list(const String& p_type) const { DVector<String> ilret; List<StringName> il; get_font_list(p_type,&il); for(List<StringName>::Element *E=il.front();E;E=E->next()) { ilret.push_back(E->get()); } return ilret; }
DVector<String> _get_color_list(const String& p_type) const { DVector<String> ilret; List<StringName> il; get_color_list(p_type,&il); for(List<StringName>::Element *E=il.front();E;E=E->next()) { ilret.push_back(E->get()); } return ilret; }
DVector<String> _get_constant_list(const String& p_type) const { DVector<String> ilret; List<StringName> il; get_constant_list(p_type,&il); for(List<StringName>::Element *E=il.front();E;E=E->next()) { ilret.push_back(E->get()); } return ilret; }
@@ -100,6 +101,7 @@ public:
bool has_stylebox(const StringName& p_name,const StringName& p_type) const;
void clear_stylebox(const StringName& p_name,const StringName& p_type);
void get_stylebox_list(StringName p_type, List<StringName> *p_list) const;
+ void get_stylebox_types(List<StringName> *p_list) const;
void set_font(const StringName& p_name,const StringName& p_type,const Ref<Font>& p_font);
Ref<Font> get_font(const StringName& p_name,const StringName& p_type) const;
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index 8cacc0fce7..e96cac170b 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -373,7 +373,15 @@ World2D::World2D() {
Physics2DServer::get_singleton()->space_set_active(space,true);
Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_GRAVITY,GLOBAL_DEF("physics_2d/default_gravity",98));
Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_GRAVITY_VECTOR,GLOBAL_DEF("physics_2d/default_gravity_vector",Vector2(0,1)));
- Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_LINEAR_DAMP,GLOBAL_DEF("physics_2d/default_density",0.1));
+ // TODO: Remove this deprecation warning and compatibility code for 2.2 or 3.0
+ if (Globals::get_singleton()->get("physics_2d/default_density") && !Globals::get_singleton()->get("physics_2d/default_linear_damp)")) {
+ WARN_PRINT("Deprecated parameter 'physics_2d/default_density'. It was renamed to 'physics_2d/default_linear_damp', adjusting your project settings accordingly (make sure to adjust scripts that potentially rely on 'physics_2d/default_density'.");
+ Globals::get_singleton()->set("physics_2d/default_linear_damp", Globals::get_singleton()->get("physics_2d/default_density"));
+ Globals::get_singleton()->set_persisting("physics_2d/default_linear_damp", true);
+ Globals::get_singleton()->set_persisting("physics_2d/default_density", false);
+ Globals::get_singleton()->save();
+ }
+ Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_LINEAR_DAMP,GLOBAL_DEF("physics_2d/default_linear_damp",0.1));
Physics2DServer::get_singleton()->area_set_param(space,Physics2DServer::AREA_PARAM_ANGULAR_DAMP,GLOBAL_DEF("physics_2d/default_angular_damp",1));
Physics2DServer::get_singleton()->space_set_param(space,Physics2DServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS,1.0);
Physics2DServer::get_singleton()->space_set_param(space,Physics2DServer::SPACE_PARAM_CONTACT_MAX_SEPARATION,1.5);
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 57bde00de4..164ae55c9f 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -48,9 +48,11 @@ SceneStringNames::SceneStringNames() {
item_rect_changed=StaticCString::create("item_rect_changed");
size_flags_changed=StaticCString::create("size_flags_changed");
minimum_size_changed=StaticCString::create("minimum_size_changed");
+ sleeping_state_changed=StaticCString::create("sleeping_state_changed");
finished=StaticCString::create("finished");
animation_changed=StaticCString::create("animation_changed");
+ animation_started=StaticCString::create("animation_started");
mouse_enter=StaticCString::create("mouse_enter");
mouse_exit=StaticCString::create("mouse_exit");
@@ -169,5 +171,16 @@ SceneStringNames::SceneStringNames() {
blend_times=StaticCString::create("blend_times");
speed=StaticCString::create("speed");
+ node_configuration_warning_changed = StaticCString::create("node_configuration_warning_changed");
+
path_pp=NodePath("..");
+
+ _default=StaticCString::create("default");
+
+ for(int i=0;i<MAX_MATERIALS;i++) {
+
+ mesh_materials[i]="material/"+itos(i);
+ }
+
+ _mesh_changed=StaticCString::create("_mesh_changed");
}
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index b2c0e9abf0..32e51ce8f4 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -62,6 +62,7 @@ public:
StringName exit_tree;
StringName size_flags_changed;
StringName minimum_size_changed;
+ StringName sleeping_state_changed;
StringName idle;
StringName iteration;
StringName update;
@@ -78,6 +79,7 @@ public:
StringName finished;
StringName animation_changed;
+ StringName animation_started;
StringName body_enter_shape;
StringName body_enter;
@@ -180,6 +182,15 @@ public:
NodePath path_pp;
+ StringName _default;
+
+ StringName node_configuration_warning_changed;
+
+ enum {
+ MAX_MATERIALS=32
+ };
+ StringName mesh_materials[MAX_MATERIALS];
+ StringName _mesh_changed;
};