diff options
28 files changed, 618 insertions, 207 deletions
diff --git a/core/script_language.h b/core/script_language.h index b0c60b4e90..cb103646ed 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -200,6 +200,34 @@ public: virtual ~ScriptInstance(); }; +struct ScriptCodeCompletionOption { + enum Kind { + KIND_CLASS, + KIND_FUNCTION, + KIND_SIGNAL, + KIND_VARIABLE, + KIND_MEMBER, + KIND_ENUM, + KIND_CONSTANT, + KIND_NODE_PATH, + KIND_FILE_PATH, + KIND_PLAIN_TEXT, + }; + Kind kind; + String display; + String insert_text; + + ScriptCodeCompletionOption() { + kind = KIND_PLAIN_TEXT; + } + + ScriptCodeCompletionOption(const String &p_text, Kind p_kind) { + display = p_text; + insert_text = p_text; + kind = p_kind; + } +}; + class ScriptCodeCompletionCache { static ScriptCodeCompletionCache *singleton; @@ -250,7 +278,7 @@ public: virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } virtual bool overrides_external_editor() { return false; } - virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { return ERR_UNAVAILABLE; } struct LookupResult { enum Type { diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 721ed23d8a..c835dda1b9 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1796,7 +1796,8 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { return TTR("Toggle this track on/off."); } - if (path_rect.has_point(p_pos)) { + // Don't overlap track keys if they start at 0. + if (path_rect.has_point(p_pos + Size2(type_icon->get_width(), 0))) { return animation->track_get_path(track); } @@ -1816,16 +1817,22 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { return TTR("Remove this track."); } - if (p_pos.x >= timeline->get_name_limit() && p_pos.x <= (get_size().width - timeline->get_buttons_width())) { + int limit = timeline->get_name_limit(); + int limit_end = get_size().width - timeline->get_buttons_width(); + // Left Border including space occupied by keyframes on t=0. + int limit_start_hitbox = limit - type_icon->get_width(); + + if (p_pos.x >= limit_start_hitbox && p_pos.x <= limit_end) { int key_idx = -1; float key_distance = 1e20; - for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { //select should happen in the opposite order of drawing for more accurate overlap select + // Select should happen in the opposite order of drawing for more accurate overlap select. + for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { Rect2 rect = const_cast<AnimationTrackEdit *>(this)->get_key_rect(i, timeline->get_zoom_scale()); float offset = animation->track_get_key_time(track, i) - timeline->get_value(); - offset = offset * timeline->get_zoom_scale() + timeline->get_name_limit(); + offset = offset * timeline->get_zoom_scale() + limit; rect.position.x += offset; if (rect.has_point(p_pos)) { @@ -1932,7 +1939,6 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { } void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { - if (p_event->is_pressed()) { if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->is_shortcut(p_event)) { emit_signal("duplicate_request"); @@ -1962,8 +1968,9 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { update(); accept_event(); } - if (path_rect.has_point(pos)) { + // Don't overlap track keys if they start at 0. + if (path_rect.has_point(pos + Size2(type_icon->get_width(), 0))) { clicking_on_name = true; accept_event(); } @@ -2033,18 +2040,21 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { accept_event(); } - //check keyframes + // Check keyframes. float scale = timeline->get_zoom_scale(); int limit = timeline->get_name_limit(); int limit_end = get_size().width - timeline->get_buttons_width(); + // Left Border including space occupied by keyframes on t=0. + int limit_start_hitbox = limit - type_icon->get_width(); - if (pos.x >= limit && pos.x <= limit_end) { + if (pos.x >= limit_start_hitbox && pos.x <= limit_end) { int key_idx = -1; float key_distance = 1e20; - for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { //select should happen in the opposite order of drawing for more accurate overlap select + // Select should happen in the opposite order of drawing for more accurate overlap select. + for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { Rect2 rect = get_key_rect(i, scale); float offset = animation->track_get_key_time(track, i) - timeline->get_value(); @@ -2060,7 +2070,7 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { key_distance = distance; } } else { - //first one does it + // First one does it. key_idx = i; break; } @@ -2071,12 +2081,11 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_command() || mb->get_shift()) { if (editor->is_key_selected(track, key_idx)) { emit_signal("deselect_key", key_idx); - } else { emit_signal("select_key", key_idx, false); moving_selection_attempt = true; select_single_attempt = -1; - moving_selection_from_ofs = (mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale(); + moving_selection_from_ofs = (mb->get_position().x - limit) / timeline->get_zoom_scale(); } } else { if (!editor->is_key_selected(track, key_idx)) { @@ -2087,24 +2096,17 @@ void AnimationTrackEdit::_gui_input(const Ref<InputEvent> &p_event) { } moving_selection_attempt = true; - moving_selection_from_ofs = (mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale(); + moving_selection_from_ofs = (mb->get_position().x - limit) / timeline->get_zoom_scale(); } accept_event(); } } - - /*using focus instead - * if (!selected && pos.x >= timeline->get_name_limit() && pos.x < (get_size().width - timeline->get_buttons_width())) { - set_selected(true); - emit_signal("selected"); - } - */ } if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { Point2 pos = mb->get_position(); if (pos.x >= timeline->get_name_limit() && pos.x <= get_size().width - timeline->get_buttons_width()) { - //can do something with menu too! show insert key + // Can do something with menu too! show insert key. float offset = (pos.x - timeline->get_name_limit()) / timeline->get_zoom_scale(); if (!menu) { menu = memnew(PopupMenu); @@ -2332,11 +2334,13 @@ void AnimationTrackEdit::set_in_group(bool p_enable) { } void AnimationTrackEdit::append_to_selection(const Rect2 &p_box, bool p_deselection) { - - Rect2 select_rect(timeline->get_name_limit(), 0, get_size().width - timeline->get_name_limit() - timeline->get_buttons_width(), get_size().height); + // Left Border including space occupied by keyframes on t=0. + int limit_start_hitbox = timeline->get_name_limit() - type_icon->get_width(); + Rect2 select_rect(limit_start_hitbox, 0, get_size().width - timeline->get_name_limit() - timeline->get_buttons_width(), get_size().height); select_rect = select_rect.clip(p_box); - for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { //select should happen in the opposite order of drawing for more accurate overlap select + // Select should happen in the opposite order of drawing for more accurate overlap select. + for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) { Rect2 rect = const_cast<AnimationTrackEdit *>(this)->get_key_rect(i, timeline->get_zoom_scale()); float offset = animation->track_get_key_time(track, i) - timeline->get_value(); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index ed50c7914e..d5aae7b562 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -724,7 +724,7 @@ void CodeTextEditor::_code_complete_timer_timeout() { void CodeTextEditor::_complete_request() { - List<String> entries; + List<ScriptCodeCompletionOption> entries; String ctext = text_editor->get_text_for_completion(); _code_complete_script(ctext, &entries); bool forced = false; @@ -733,15 +733,16 @@ void CodeTextEditor::_complete_request() { } if (entries.size() == 0) return; - Vector<String> strs; - strs.resize(entries.size()); - int i = 0; - for (List<String>::Element *E = entries.front(); E; E = E->next()) { - strs.write[i++] = E->get(); + Vector<String> options; + options.resize(entries.size()); + size_t i = 0; + for (List<ScriptCodeCompletionOption>::Element *E = entries.front(); E; E = E->next()) { + options.write[i] = E->get().insert_text; + i++; } - text_editor->code_complete(strs, forced); + text_editor->code_complete(options, forced); } void CodeTextEditor::_font_resize_timeout() { diff --git a/editor/code_editor.h b/editor/code_editor.h index c0989f9704..2653a8cecc 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -131,7 +131,7 @@ public: FindReplaceBar(); }; -typedef void (*CodeTextEditorCodeCompleteFunc)(void *p_ud, const String &p_code, List<String> *r_options, bool &r_forced); +typedef void (*CodeTextEditorCodeCompleteFunc)(void *p_ud, const String &p_code, List<ScriptCodeCompletionOption> *r_options, bool &r_forced); class CodeTextEditor : public VBoxContainer { @@ -183,7 +183,7 @@ class CodeTextEditor : public VBoxContainer { protected: virtual void _load_theme_settings() {} virtual void _validate_script() {} - virtual void _code_complete_script(const String &p_code, List<String> *r_options) {} + virtual void _code_complete_script(const String &p_code, List<ScriptCodeCompletionOption> *r_options) {} void _text_changed_idle_timeout(); void _code_complete_timer_timeout(); diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index 88dc258c5f..1ae5acc5ff 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -95,6 +95,7 @@ void PathSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_p Vector3 ray_from = p_camera->project_ray_origin(p_point); Vector3 ray_dir = p_camera->project_ray_normal(p_point); + // Setting curve point positions if (p_idx < c->get_point_count()) { Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2)); @@ -126,6 +127,7 @@ void PathSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_p Vector3 inters; + // Setting curve in/out positions if (p.intersects_ray(ray_from, ray_dir, &inters)) { if (!PathEditorPlugin::singleton->is_handle_clicked()) { @@ -135,9 +137,13 @@ void PathSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_p } Vector3 local = gi.xform(inters) - base; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + float snap = SpatialEditor::get_singleton()->get_translate_snap(); + local.snap(Vector3(snap, snap, snap)); + } + if (t == 0) { c->set_point_in(idx, local); - if (PathEditorPlugin::singleton->mirror_angle_enabled()) c->set_point_out(idx, PathEditorPlugin::singleton->mirror_length_enabled() ? -local : (-local.normalized() * orig_out_length)); } else { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 2cfa759e3b..438621115b 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -773,13 +773,13 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo } } -void ScriptTextEditor::_code_complete_scripts(void *p_ud, const String &p_code, List<String> *r_options, bool &r_force) { +void ScriptTextEditor::_code_complete_scripts(void *p_ud, const String &p_code, List<ScriptCodeCompletionOption> *r_options, bool &r_force) { ScriptTextEditor *ste = (ScriptTextEditor *)p_ud; ste->_code_complete_script(p_code, r_options, r_force); } -void ScriptTextEditor::_code_complete_script(const String &p_code, List<String> *r_options, bool &r_force) { +void ScriptTextEditor::_code_complete_script(const String &p_code, List<ScriptCodeCompletionOption> *r_options, bool &r_force) { if (color_panel->is_visible_in_tree()) return; Node *base = get_tree()->get_edited_scene_root(); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index b53383b117..9a2a514a6e 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -143,7 +143,6 @@ class ScriptTextEditor : public ScriptEditorBase { }; protected: - static void _code_complete_scripts(void *p_ud, const String &p_code, List<String> *r_options, bool &r_force); void _update_breakpoint_list(); void _breakpoint_item_pressed(int p_idx); void _breakpoint_toggled(int p_row); @@ -151,7 +150,10 @@ protected: void _validate_script(); // No longer virtual. void _update_bookmark_list(); void _bookmark_item_pressed(int p_idx); - void _code_complete_script(const String &p_code, List<String> *r_options, bool &r_force); + + static void _code_complete_scripts(void *p_ud, const String &p_code, List<ScriptCodeCompletionOption> *r_options, bool &r_force); + void _code_complete_script(const String &p_code, List<ScriptCodeCompletionOption> *r_options, bool &r_force); + void _load_theme_settings(); void _set_theme_for_script(); void _show_warnings_panel(bool p_show); diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index fe6d9dd8c7..cbfd0f3742 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -873,6 +873,10 @@ void LightSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); float d = -ra.z; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0) d = 0; @@ -885,6 +889,10 @@ void LightSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, if (cp.intersects_ray(ray_from, ray_dir, &inters)) { float r = inters.distance_to(gt.origin); + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + r = Math::stepify(r, SpatialEditor::get_singleton()->get_translate_snap()); + } + light->set_param(Light::PARAM_RANGE, r); } } @@ -1257,6 +1265,10 @@ void CameraSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); float d = ra.x * 2.0; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0) d = 0; @@ -2310,6 +2322,9 @@ void VisibilityNotifierGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; notifier->set_aabb(aabb); @@ -2319,6 +2334,10 @@ void VisibilityNotifierGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx] - ofs[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; //resize @@ -2444,7 +2463,6 @@ void ParticlesGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Ca Particles *particles = Object::cast_to<Particles>(p_gizmo->get_spatial_node()); Transform gt = particles->get_global_transform(); - //gt.orthonormalize(); Transform gi = gt.affine_inverse(); bool move = p_idx >= 3; @@ -2467,6 +2485,9 @@ void ParticlesGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Ca Geometry::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; particles->set_visibility_aabb(aabb); @@ -2476,6 +2497,10 @@ void ParticlesGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Ca Geometry::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx] - ofs[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; //resize @@ -2619,6 +2644,10 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -2641,8 +2670,11 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_i Vector3 ra, rb; Geometry::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb); - float d = ra[p_idx]; - d += 0.25; + // Adjust the actual position to account for the gizmo handle position + float d = ra[p_idx] + 0.25; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } origin[p_idx] = d; probe->set_origin_offset(origin); @@ -2780,7 +2812,6 @@ void GIProbeGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Came GIProbe *probe = Object::cast_to<GIProbe>(p_gizmo->get_spatial_node()); Transform gt = probe->get_global_transform(); - //gt.orthonormalize(); Transform gi = gt.affine_inverse(); Vector3 extents = probe->get_extents(); @@ -2796,6 +2827,10 @@ void GIProbeGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Came Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -2945,7 +2980,6 @@ void BakedIndirectLightGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int BakedLightmap *baker = Object::cast_to<BakedLightmap>(p_gizmo->get_spatial_node()); Transform gt = baker->get_global_transform(); - //gt.orthonormalize(); Transform gi = gt.affine_inverse(); Vector3 extents = baker->get_extents(); @@ -2961,6 +2995,10 @@ void BakedIndirectLightGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -3158,6 +3196,10 @@ void CollisionShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); float d = ra.x; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -3170,6 +3212,10 @@ void CollisionShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); float d = ra.z; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -3184,6 +3230,10 @@ void CollisionShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = ra[p_idx]; + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -3202,6 +3252,11 @@ void CollisionShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, i float d = axis.dot(ra); if (p_idx == 1) d -= cs2->get_radius(); + + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } + if (d < 0.001) d = 0.001; @@ -3219,6 +3274,9 @@ void CollisionShapeSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, i Vector3 ra, rb; Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); float d = axis.dot(ra); + if (SpatialEditor::get_singleton()->is_snap_enabled()) { + d = Math::stepify(d, SpatialEditor::get_singleton()->get_translate_snap()); + } if (d < 0.001) d = 0.001; diff --git a/modules/gdnative/pluginscript/pluginscript_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp index 4bbadc62e7..9de073fc8e 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.cpp +++ b/modules/gdnative/pluginscript/pluginscript_language.cpp @@ -159,7 +159,7 @@ String PluginScriptLanguage::make_function(const String &p_class, const String & return String(); } -Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint) { +Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) { if (_desc.complete_code) { Array options; godot_error tmp = _desc.complete_code( @@ -171,7 +171,8 @@ Error PluginScriptLanguage::complete_code(const String &p_code, const String &p_ &r_force, (godot_string *)&r_call_hint); for (int i = 0; i < options.size(); i++) { - r_options->push_back(String(options[i])); + ScriptCodeCompletionOption option(options[i], ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_options->push_back(option); } return (Error)tmp; } diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h index a11e916975..7b3844d0b0 100644 --- a/modules/gdnative/pluginscript/pluginscript_language.h +++ b/modules/gdnative/pluginscript/pluginscript_language.h @@ -81,7 +81,7 @@ public: virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_force, String &r_call_hint); + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint); virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const; virtual void add_global_constant(const StringName &p_variable, const Variant &p_value); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 1756f6eabc..a5ad23c75d 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -458,7 +458,7 @@ public: virtual bool can_inherit_from_file() { return true; } virtual int find_function(const String &p_function, const String &p_code) const; virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const; - virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint); + virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint); #ifdef TOOLS_ENABLED virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result); #endif diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 26b14a6148..78a1bfc99b 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -509,12 +509,14 @@ struct GDScriptCompletionIdentifier { assigned_expression(NULL) {} }; -static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) { +static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) { const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; for (int i = 0; i < p_dir->get_file_count(); i++) { - r_list.insert(quote_style + p_dir->get_file_path(i) + quote_style); + ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH); + option.insert_text = quote_style + option.display + quote_style; + r_list.insert(option.display, option); } for (int i = 0; i < p_dir->get_subdir_count(); i++) { @@ -1807,14 +1809,15 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio return arghint; } -static void _find_enumeration_candidates(const String p_enum_hint, Set<String> &r_result) { +static void _find_enumeration_candidates(const String p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) { if (p_enum_hint.find(".") == -1) { // Global constant StringName current_enum = p_enum_hint; for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) { if (GlobalConstants::get_global_constant_enum(i) == current_enum) { - r_result.insert(GlobalConstants::get_global_constant_name(i)); + ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM); + r_result.insert(option.display, option); } } } else { @@ -1829,15 +1832,17 @@ static void _find_enumeration_candidates(const String p_enum_hint, Set<String> & ClassDB::get_enum_constants(class_name, enum_name, &enum_constants); for (List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) { String candidate = class_name + "." + E->get(); - r_result.insert(candidate); + ScriptCodeCompletionOption option(candidate, ScriptCodeCompletionOption::KIND_ENUM); + r_result.insert(option.display, option); } } } -static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Set<String> &r_result) { +static void _find_identifiers_in_block(const GDScriptCompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) { for (Map<StringName, GDScriptParser::LocalVarNode *>::Element *E = p_context.block->variables.front(); E; E = E->next()) { if (E->get()->line < p_context.line) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_VARIABLE); + r_result.insert(option.display, option); } } if (p_context.block->parent_block) { @@ -1847,40 +1852,47 @@ static void _find_identifiers_in_block(const GDScriptCompletionContext &p_contex } } -static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result); +static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result); -static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Set<String> &r_result) { +static void _find_identifiers_in_class(const GDScriptCompletionContext &p_context, bool p_static, bool p_only_functions, bool p_parent_only, Map<String, ScriptCodeCompletionOption> &r_result) { if (!p_parent_only) { if (!p_static && !p_only_functions) { for (int i = 0; i < p_context._class->variables.size(); i++) { - r_result.insert(p_context._class->variables[i].identifier); + ScriptCodeCompletionOption option(p_context._class->variables[i].identifier, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_context._class->constant_expressions.front(); E; E = E->next()) { - r_result.insert(E->key()); + ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } for (int i = 0; i < p_context._class->subclasses.size(); i++) { - r_result.insert(p_context._class->subclasses[i]->name); + ScriptCodeCompletionOption option(p_context._class->subclasses[i]->name, ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } } for (int i = 0; i < p_context._class->static_functions.size(); i++) { + ScriptCodeCompletionOption option(p_context._class->static_functions[i]->name.operator String(), ScriptCodeCompletionOption::KIND_FUNCTION); if (p_context._class->static_functions[i]->arguments.size()) { - r_result.insert(p_context._class->static_functions[i]->name.operator String() + "("); + option.insert_text += "("; } else { - r_result.insert(p_context._class->static_functions[i]->name.operator String() + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } if (!p_static) { for (int i = 0; i < p_context._class->functions.size(); i++) { + ScriptCodeCompletionOption option(p_context._class->functions[i]->name.operator String(), ScriptCodeCompletionOption::KIND_FUNCTION); if (p_context._class->functions[i]->arguments.size()) { - r_result.insert(p_context._class->functions[i]->name.operator String() + "("); + option.insert_text += "("; } else { - r_result.insert(p_context._class->functions[i]->name.operator String() + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } } } @@ -1898,12 +1910,14 @@ static void _find_identifiers_in_class(const GDScriptCompletionContext &p_contex _find_identifiers_in_base(c, base_type, p_only_functions, r_result); } -static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Set<String> &r_result) { +static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) { GDScriptParser::DataType base_type = p_base.type; bool _static = base_type.is_meta_type; if (_static && base_type.kind != GDScriptParser::DataType::BUILTIN) { - r_result.insert("new("); + ScriptCodeCompletionOption option("new", ScriptCodeCompletionOption::KIND_FUNCTION); + option.insert_text += "("; + r_result.insert(option.display, option); } while (base_type.has_type) { @@ -1921,26 +1935,31 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (script.is_valid()) { if (!_static && !p_only_functions) { for (const Set<StringName>::Element *E = script->get_members().front(); E; E = E->next()) { - r_result.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { for (const Map<StringName, Variant>::Element *E = script->get_constants().front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } } for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) { if (!_static || E->get()->is_static()) { + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get()->get_argument_count()) { - r_result.insert(E->key().operator String() + "("); + option.insert_text += "("; } else { - r_result.insert(E->key().operator String() + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } } if (!p_only_functions) { for (const Map<StringName, Ref<GDScript> >::Element *E = script->get_subclasses().front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } } base_type = GDScriptParser::DataType(); @@ -1964,25 +1983,29 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context List<PropertyInfo> members; scr->get_script_property_list(&members); for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) { - r_result.insert(E->get().name); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } if (!p_only_functions) { Map<StringName, Variant> constants; scr->get_constants(&constants); for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } } List<MethodInfo> methods; scr->get_script_method_list(&methods); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get().arguments.size()) { - r_result.insert(E->get().name + "("); + option.insert_text += "("; } else { - r_result.insert(E->get().name + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } Ref<Script> base_script = scr->get_base_script(); @@ -2009,7 +2032,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context List<String> constants; ClassDB::get_integer_constant_list(type, &constants); for (List<String>::Element *E = constants.front(); E; E = E->next()) { - r_result.insert(E->get()); + ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } if (!_static) { @@ -2022,7 +2046,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (E->get().name.find("/") != -1) { continue; } - r_result.insert(E->get().name); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } } @@ -2035,11 +2060,13 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context if (E->get().name.begins_with("_")) { continue; } + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get().arguments.size()) { - r_result.insert(E->get().name + "("); + option.insert_text += "("; } else { - r_result.insert(E->get().name + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } } @@ -2058,7 +2085,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) { if (String(E->get().name).find("/") == -1) { - r_result.insert(E->get().name); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER); + r_result.insert(option.display, option); } } } @@ -2066,11 +2094,13 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context List<MethodInfo> methods; tmp.get_method_list(&methods); for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION); if (E->get().arguments.size()) { - r_result.insert(E->get().name + "("); + option.insert_text += "("; } else { - r_result.insert(E->get().name + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } return; @@ -2082,7 +2112,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionContext &p_context } } -static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Set<String> &r_result) { +static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p_only_functions, Map<String, ScriptCodeCompletionOption> &r_result) { const GDScriptParser::BlockNode *block = p_context.block; @@ -2091,7 +2121,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p const GDScriptParser::FunctionNode *f = p_context.function; for (int i = 0; i < f->arguments.size(); i++) { - r_result.insert(f->arguments[i].operator String()); + ScriptCodeCompletionOption option(f->arguments[i].operator String(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_result.insert(option.display, option); } } @@ -2116,11 +2147,13 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { MethodInfo mi = GDScriptFunctions::get_info(GDScriptFunctions::Function(i)); + ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION); if (mi.arguments.size() || (mi.flags & METHOD_FLAG_VARARG)) { - r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "("); + option.insert_text += "("; } else { - r_result.insert(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) + "()"); + option.insert_text += "()"; } + r_result.insert(option.display, option); } static const char *_type_names[Variant::VARIANT_MAX] = { @@ -2130,7 +2163,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p }; for (int i = 0; i < Variant::VARIANT_MAX; i++) { - r_result.insert(_type_names[i]); + ScriptCodeCompletionOption option(_type_names[i], ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } static const char *_keywords[] = { @@ -2144,7 +2178,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p const char **kw = _keywords; while (*kw) { - r_result.insert(*kw); + ScriptCodeCompletionOption option(*kw, ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + r_result.insert(option.display, option); kw++; } @@ -2158,7 +2193,8 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p } String path = ProjectSettings::get_singleton()->get(s); if (path.begins_with("*")) { - r_result.insert(s.get_slice("/", 1)); + ScriptCodeCompletionOption option(s.get_slice("/", 1), ScriptCodeCompletionOption::KIND_CONSTANT); + r_result.insert(option.display, option); } } @@ -2166,16 +2202,18 @@ static void _find_identifiers(const GDScriptCompletionContext &p_context, bool p List<StringName> named_scripts; ScriptServer::get_global_class_list(&named_scripts); for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) { - r_result.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } // Native classes for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) { - r_result.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + r_result.insert(option.display, option); } } -static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Set<String> &r_result, String &r_arghint) { +static void _find_call_arguments(const GDScriptCompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptCodeCompletionOption> &r_result, String &r_arghint) { Variant base = p_base.value; GDScriptParser::DataType base_type = p_base.type; @@ -2199,7 +2237,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con if ((p_method == "connect" || p_method == "emit_signal") && p_argidx == 0) { for (int i = 0; i < base_type.class_type->_signals.size(); i++) { - r_result.insert(quote_style + base_type.class_type->_signals[i].name.operator String() + quote_style); + ScriptCodeCompletionOption option(base_type.class_type->_signals[i].name.operator String(), ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2212,7 +2252,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<MethodInfo> signals; gds->get_script_signal_list(&signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - r_result.insert(quote_style + E->get().name + quote_style); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } Ref<GDScript> base_script = gds->get_base_script(); @@ -2250,7 +2292,8 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<String> options; obj->get_argument_options(p_method, p_argidx, &options); for (List<String>::Element *F = options.front(); F; F = F->next()) { - r_result.insert(F->get()); + ScriptCodeCompletionOption option(F->get(), ScriptCodeCompletionOption::KIND_FUNCTION); + r_result.insert(option.display, option); } } } @@ -2271,7 +2314,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con List<MethodInfo> signals; ClassDB::get_signal_list(class_name, &signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - r_result.insert(quote_style + E->get().name + quote_style); + ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2286,7 +2331,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con continue; } String name = s.get_slice("/", 1); - r_result.insert(quote_style + "/root/" + name + quote_style); + ScriptCodeCompletionOption option("/root/" + name, ScriptCodeCompletionOption::KIND_NODE_PATH); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2300,7 +2347,9 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con continue; } String name = s.get_slice("/", 1); - r_result.insert(quote_style + name + quote_style); + ScriptCodeCompletionOption option(name, ScriptCodeCompletionOption::KIND_CONSTANT); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } @@ -2333,7 +2382,7 @@ static void _find_call_arguments(const GDScriptCompletionContext &p_context, con } } -static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Set<String> &r_result, bool &r_forced, String &r_arghint) { +static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDScriptParser::Node *p_node, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) { const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; @@ -2451,17 +2500,19 @@ static void _find_call_arguments(GDScriptCompletionContext &p_context, const GDS _find_call_arguments(p_context, ci, function, p_argidx, _static, r_result, r_arghint); if (function == "connect" && p_argidx == 2) { - Set<String> methods; + Map<String, ScriptCodeCompletionOption> methods; _find_identifiers_in_base(p_context, connect_base, true, methods); - for (Set<String>::Element *E = methods.front(); E; E = E->next()) { - r_result.insert(quote_style + E->get().replace("(", "").replace(")", "") + quote_style); + for (Map<String, ScriptCodeCompletionOption>::Element *E = methods.front(); E; E = E->next()) { + ScriptCodeCompletionOption &option = E->value(); + option.insert_text = quote_style + option.display + quote_style; + r_result.insert(option.display, option); } } r_forced = r_result.size() > 0; } -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\""; @@ -2469,7 +2520,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path parser.parse(p_code, p_path.get_base_dir(), false, p_path, true); r_forced = false; - Set<String> options; + Map<String, ScriptCodeCompletionOption> options; GDScriptCompletionContext context; context._class = parser.get_completion_class(); context.block = parser.get_completion_block(); @@ -2490,7 +2541,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path List<StringName> constants; Variant::get_constants_for_type(parser.get_completion_built_in_constant(), &constants); for (List<StringName>::Element *E = constants.front(); E; E = E->next()) { - options.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT); + options.insert(option.display, option); } } break; case GDScriptParser::COMPLETION_PARENT_FUNCTION: { @@ -2515,9 +2567,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path r_forced = true; String idopt = opt.unquote(); if (idopt.replace("/", "_").is_valid_identifier()) { - options.insert(idopt); + ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } else { - options.insert(opt); + ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } } } @@ -2532,7 +2586,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path continue; } String name = s.get_slice("/", 1); - options.insert(quote_style + "/root/" + name + quote_style); + ScriptCodeCompletionOption option(quote_style + "/root/" + name + quote_style, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } } } break; @@ -2655,7 +2710,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path } method_hint += ":"; - options.insert(method_hint); + ScriptCodeCompletionOption option(method_hint, ScriptCodeCompletionOption::KIND_FUNCTION); + options.insert(option.display, option); } } break; case GDScriptParser::COMPLETION_YIELD: { @@ -2673,7 +2729,9 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path switch (base_type.kind) { case GDScriptParser::DataType::CLASS: { for (int i = 0; i < base_type.class_type->_signals.size(); i++) { - options.insert(quote_style + base_type.class_type->_signals[i].name.operator String() + quote_style); + ScriptCodeCompletionOption option(base_type.class_type->_signals[i].name.operator String(), ScriptCodeCompletionOption::KIND_SIGNAL); + option.insert_text = quote_style + option.display + quote_style; + options.insert(option.display, option); } base_type = base_type.class_type->base_type; } break; @@ -2684,7 +2742,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path List<MethodInfo> signals; scr->get_script_signal_list(&signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - options.insert(quote_style + E->get().name + quote_style); + ScriptCodeCompletionOption option(quote_style + E->get().name + quote_style, ScriptCodeCompletionOption::KIND_SIGNAL); + options.insert(option.display, option); } Ref<Script> base_script = scr->get_base_script(); if (base_script.is_valid()) { @@ -2711,7 +2770,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path List<MethodInfo> signals; ClassDB::get_signal_list(class_name, &signals); for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - options.insert(quote_style + E->get().name + quote_style); + ScriptCodeCompletionOption option(quote_style + E->get().name + quote_style, ScriptCodeCompletionOption::KIND_SIGNAL); + options.insert(option.display, option); } } break; default: { @@ -2748,18 +2808,21 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path c.line = E->value().expression->line; if (_guess_expression_type(c, E->value().expression, constant)) { if (constant.type.has_type && constant.type.is_meta_type) { - options.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } } for (int i = 0; i < clss->subclasses.size(); i++) { if (clss->subclasses[i]->name != StringName()) { - options.insert(clss->subclasses[i]->name.operator String()); + ScriptCodeCompletionOption option(clss->subclasses[i]->name.operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } clss = clss->owner; for (int i = 0; i < Variant::VARIANT_MAX; i++) { - options.insert(Variant::get_type_name((Variant::Type)i)); + ScriptCodeCompletionOption option(Variant::get_type_name((Variant::Type)i), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } @@ -2773,18 +2836,21 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path if (Engine::get_singleton()->has_singleton(class_name)) { continue; } - options.insert(class_name); + ScriptCodeCompletionOption option(class_name, ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } // Named scripts List<StringName> named_scripts; ScriptServer::get_global_class_list(&named_scripts); for (List<StringName>::Element *E = named_scripts.front(); E; E = E->next()) { - options.insert(E->get().operator String()); + ScriptCodeCompletionOption option(E->get().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } if (parser.get_completion_identifier_is_function()) { - options.insert("void"); + ScriptCodeCompletionOption option("void", ScriptCodeCompletionOption::KIND_PLAIN_TEXT); + options.insert(option.display, option); } r_forced = true; } break; @@ -2831,13 +2897,15 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path c2.line = E->value().expression->line; if (_guess_expression_type(c2, E->value().expression, constant)) { if (constant.type.has_type && constant.type.is_meta_type) { - options.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } } for (int i = 0; i < base_type.class_type->subclasses.size(); i++) { if (base_type.class_type->subclasses[i]->name != StringName()) { - options.insert(base_type.class_type->subclasses[i]->name.operator String()); + ScriptCodeCompletionOption option(base_type.class_type->subclasses[i]->name.operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } @@ -2855,7 +2923,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { Ref<Script> const_scr = E->value(); if (const_scr.is_valid()) { - options.insert(E->key().operator String()); + ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS); + options.insert(option.display, option); } } Ref<Script> base_script = scr->get_base_script(); @@ -2877,7 +2946,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path } break; } - for (Set<String>::Element *E = options.front(); E; E = E->next()) { + for (Map<String, ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) { r_options->push_back(E->get()); } @@ -2886,7 +2955,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path #else -Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<String> *r_options, bool &r_forced, String &r_call_hint) { +Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { return OK; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 9ab86a5459..c5c86fda0a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -125,7 +125,7 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { } } -bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete) { +bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete, bool p_parsing_constant) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { tokenizer->advance(); @@ -149,7 +149,7 @@ bool GDScriptParser::_parse_arguments(Node *p_parent, Vector<Node *> &p_args, bo return false; } - Node *arg = _parse_expression(p_parent, p_static); + Node *arg = _parse_expression(p_parent, p_static, false, p_parsing_constant); if (!arg) { return false; } @@ -639,7 +639,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s id->name = identifier; op->arguments.push_back(id); - if (!_parse_arguments(op, op->arguments, p_static, true)) + if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) return NULL; expr = op; @@ -731,7 +731,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s completion_node = op; } if (!replaced) { - if (!_parse_arguments(op, op->arguments, p_static, true)) + if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) return NULL; expr = op; } @@ -826,11 +826,12 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } // Check parents for the constant - if (!bfn && cln->extends_file != StringName()) { - Ref<GDScript> parent = ResourceLoader::load(cln->extends_file); - if (parent.is_valid() && parent->is_valid()) { + if (!bfn) { + // Using current_class instead of cln here, since cln is const* + _determine_inheritance(current_class, false); + if (cln->base_type.has_type && cln->base_type.kind == DataType::GDSCRIPT && cln->base_type.script_type->is_valid()) { Map<StringName, Variant> parent_constants; - parent->get_constants(&parent_constants); + current_class->base_type.script_type->get_constants(&parent_constants); if (parent_constants.has(identifier)) { ConstantNode *constant = alloc_node<ConstantNode>(); constant->value = parent_constants[identifier]; @@ -1112,7 +1113,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s } } else { tokenizer->advance(); - if (!_parse_arguments(op, op->arguments, p_static)) { + if (!_parse_arguments(op, op->arguments, p_static, false, p_parsing_constant)) { return NULL; } } @@ -1164,22 +1165,14 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s tokenizer->advance(); IdentifierNode *id = alloc_node<IdentifierNode>(); - if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_FUNC) { - //small hack so built in funcs don't obfuscate methods - - id->name = GDScriptFunctions::get_func_name(tokenizer->get_token_built_in_func()); - tokenizer->advance(); - - } else { - StringName identifier; - if (_get_completable_identifier(COMPLETION_METHOD, identifier)) { - completion_node = op; - //indexing stuff - } - - id->name = identifier; + StringName identifier; + if (_get_completable_identifier(COMPLETION_METHOD, identifier)) { + completion_node = op; + //indexing stuff } + id->name = identifier; + op->arguments.push_back(expr); // call what op->arguments.push_back(id); // call func //get arguments @@ -1188,7 +1181,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s _make_completable_call(0); completion_node = op; } - if (!_parse_arguments(op, op->arguments, p_static, true)) + if (!_parse_arguments(op, op->arguments, p_static, true, p_parsing_constant)) return NULL; expr = op; @@ -5158,9 +5151,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } -void GDScriptParser::_determine_inheritance(ClassNode *p_class) { +void GDScriptParser::_determine_inheritance(ClassNode *p_class, bool p_recursive) { - if (p_class->extends_used) { + if (p_class->base_type.has_type) { + // Already determined + } else if (p_class->extends_used) { //do inheritance String path = p_class->extends_file; @@ -5355,9 +5350,11 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class) { p_class->base_type.native_type = "Reference"; } - // Recursively determine subclasses - for (int i = 0; i < p_class->subclasses.size(); i++) { - _determine_inheritance(p_class->subclasses[i]); + if (p_recursive) { + // Recursively determine subclasses + for (int i = 0; i < p_class->subclasses.size(); i++) { + _determine_inheritance(p_class->subclasses[i], p_recursive); + } } } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 5e4de11357..62d7bdb393 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -582,7 +582,7 @@ private: #endif // DEBUG_ENABLED bool _recover_from_completion(); - bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false); + bool _parse_arguments(Node *p_parent, Vector<Node *> &p_args, bool p_static, bool p_can_codecomplete = false, bool p_parsing_constant = false); bool _enter_indent_block(BlockNode *p_block = NULL); bool _parse_newline(); Node *_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign = false, bool p_parsing_constant = false); @@ -599,7 +599,7 @@ private: void _parse_class(ClassNode *p_class); bool _end_statement(); - void _determine_inheritance(ClassNode *p_class); + void _determine_inheritance(ClassNode *p_class, bool p_recursive = true); bool _parse_type(DataType &r_type, bool p_can_be_void = false); DataType _resolve_type(const DataType &p_source, int p_line); DataType _type_from_variant(const Variant &p_value) const; diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py new file mode 100644 index 0000000000..cd9210897d --- /dev/null +++ b/modules/mono/build_scripts/make_android_mono_config.py @@ -0,0 +1,69 @@ + +def generate_compressed_config(config_src, output_dir): + import os.path + from compat import byte_to_str + + # Header file + with open(os.path.join(output_dir, 'android_mono_config.gen.h'), 'w') as header: + header.write('''/* THIS FILE IS GENERATED DO NOT EDIT */ +#ifndef ANDROID_MONO_CONFIG_GEN_H +#define ANDROID_MONO_CONFIG_GEN_H + +#ifdef ANDROID_ENABLED + +#include "core/ustring.h" + +String get_godot_android_mono_config(); + +#endif // ANDROID_ENABLED + +#endif // ANDROID_MONO_CONFIG_GEN_H +''') + + # Source file + with open(os.path.join(output_dir, 'android_mono_config.gen.cpp'), 'w') as cpp: + with open(config_src, 'rb') as f: + buf = f.read() + decompr_size = len(buf) + import zlib + buf = zlib.compress(buf) + compr_size = len(buf) + + bytes_seq_str = '' + for i, buf_idx in enumerate(range(compr_size)): + if i > 0: + bytes_seq_str += ', ' + bytes_seq_str += byte_to_str(buf[buf_idx]) + + cpp.write('''/* THIS FILE IS GENERATED DO NOT EDIT */ +#include "android_mono_config.gen.h" + +#ifdef ANDROID_ENABLED + +#include "core/io/compression.h" +#include "core/pool_vector.h" + +namespace { + +// config +static const int config_compressed_size = %d; +static const int config_uncompressed_size = %d; +static const unsigned char config_compressed_data[] = { %s }; + +} // namespace + +String get_godot_android_mono_config() { + PoolVector<uint8_t> data; + data.resize(config_uncompressed_size); + PoolVector<uint8_t>::Write w = data.write(); + Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data, + config_compressed_size, Compression::MODE_DEFLATE); + String s; + if (s.parse_utf8((const char *)w.ptr(), data.size())) { + ERR_FAIL_V(String()); + } + return s; +} + +#endif // ANDROID_ENABLED +''' % (compr_size, decompr_size, bytes_seq_str)) diff --git a/modules/mono/build_scripts/make_cs_compressed_header.py b/modules/mono/build_scripts/make_cs_compressed_header.py index 1f9177cef8..ed49db5bb2 100644 --- a/modules/mono/build_scripts/make_cs_compressed_header.py +++ b/modules/mono/build_scripts/make_cs_compressed_header.py @@ -23,30 +23,31 @@ def generate_header(src, dst, version_dst): latest_mtime = mtime if mtime > latest_mtime else latest_mtime with open(filepath, 'rb') as f: buf = f.read() - decomp_size = len(buf) + decompr_size = len(buf) import zlib buf = zlib.compress(buf) + compr_size = len(buf) name = str(cs_file_count) header.write('\n') header.write('// ' + filepath_src_rel + '\n') - header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n') - header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n') + header.write('static const int _cs_' + name + '_compressed_size = ' + str(compr_size) + ';\n') + header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decompr_size) + ';\n') header.write('static const unsigned char _cs_' + name + '_compressed[] = { ') - for i, buf_idx in enumerate(range(len(buf))): + for i, buf_idx in enumerate(range(compr_size)): if i > 0: header.write(', ') header.write(byte_to_str(buf[buf_idx])) + header.write(' };\n') inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \ - 'CompressedFile(_cs_' + name + '_compressed_size, ' \ + 'GodotCsCompressedFile(_cs_' + name + '_compressed_size, ' \ '_cs_' + name + '_uncompressed_size, ' \ '_cs_' + name + '_compressed));\n' - header.write(' };\n') - header.write('\nstruct CompressedFile\n' '{\n' + header.write('\nstruct GodotCsCompressedFile\n' '{\n' '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n' - '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' + '\n\tGodotCsCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n' '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n' - '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n' - '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' + '\t\tdata = p_data;\n' '\t}\n' '\n\tGodotCsCompressedFile() {}\n' '};\n' + '\nvoid get_compressed_files(Map<String, GodotCsCompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n' ) header.write('\n#endif // TOOLS_ENABLED\n') header.write('\n#endif // CS_COMPRESSED_H\n') diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 733cdd168c..3b15f722a3 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -41,7 +41,7 @@ def copy_file(src_dir, dst_dir, name): dst_dir = Dir(dst_dir).abspath if not os.path.isdir(dst_dir): - os.mkdir(dst_dir) + os.makedirs(dst_dir) copy(src_path, dst_dir) @@ -65,6 +65,10 @@ def configure(env, env_mono): # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders. raise RuntimeError('This module does not currently support building for android with tools enabled') + if is_android and mono_static: + # When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0 + raise RuntimeError('Linking Mono statically is not currently supported on Android') + if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix: print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead") @@ -188,7 +192,7 @@ def configure(env, env_mono): if is_apple: env.Append(LIBS=['iconv', 'pthread']) elif is_android: - env.Append(LIBS=['m', 'dl']) + pass # Nothing else: env.Append(LIBS=['m', 'rt', 'dl', 'pthread']) @@ -236,6 +240,14 @@ def configure(env, env_mono): mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip() make_template_dir(env, mono_root) + elif not tools_enabled and is_android: + # Compress Android Mono Config + from . import make_android_mono_config + config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config') + make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/') + + # Copy the required shared libraries + copy_mono_shared_libs(env, mono_root, None) if copy_mono_root: if not mono_root: @@ -270,9 +282,8 @@ def make_template_dir(env, mono_root): # Copy etc/mono/ - if platform != 'android': - template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') - copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) + template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono') + copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform']) # Copy the required shared libraries diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 9522eaee77..cdec10eaba 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -682,19 +682,20 @@ bool CSharpLanguage::is_assembly_reloading_needed() { GDMonoAssembly *proj_assembly = gdmono->get_project_assembly(); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; } - name += ".dll"; + appname_safe += ".dll"; if (proj_assembly) { String proj_asm_path = proj_assembly->get_path(); if (!FileAccess::exists(proj_assembly->get_path())) { // Maybe it wasn't loaded from the default path, so check this as well - proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name); + proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe); if (!FileAccess::exists(proj_asm_path)) return false; // No assembly to load } @@ -702,7 +703,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() { if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time()) return false; // Already up to date } else { - if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name))) + if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe))) return false; // No assembly to load } diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 2d618f7891..5e0ce3554a 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -914,12 +914,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, // Generate sources from compressed files - Map<String, CompressedFile> compressed_files; + Map<String, GodotCsCompressedFile> compressed_files; get_compressed_files(compressed_files); - for (Map<String, CompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) { + for (Map<String, GodotCsCompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) { const String &file_name = E->key(); - const CompressedFile &file_data = E->value(); + const GodotCsCompressedFile &file_data = E->value(); String output_file = path_join(core_dir, file_name); diff --git a/modules/mono/editor/godotsharp_editor.cpp b/modules/mono/editor/godotsharp_editor.cpp index 9d42528927..8dc5ced29f 100644 --- a/modules/mono/editor/godotsharp_editor.cpp +++ b/modules/mono/editor/godotsharp_editor.cpp @@ -63,16 +63,17 @@ bool GodotSharpEditor::_create_project_solution() { pr.step(TTR("Generating C# project...")); String path = OS::get_singleton()->get_resource_dir(); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; } - String guid = CSharpProject::generate_game_project(path, name); + String guid = CSharpProject::generate_game_project(path, appname_safe); if (guid.length()) { - DotNetSolution solution(name); + DotNetSolution solution(appname_safe); if (!solution.set_path(path)) { show_error_dialog(TTR("Failed to create solution.")); @@ -81,12 +82,12 @@ bool GodotSharpEditor::_create_project_solution() { DotNetSolution::ProjectInfo proj_info; proj_info.guid = guid; - proj_info.relpath = name + ".csproj"; + proj_info.relpath = appname_safe + ".csproj"; proj_info.configs.push_back("Debug"); proj_info.configs.push_back("Release"); proj_info.configs.push_back("Tools"); - solution.add_new_project(name, proj_info); + solution.add_new_project(appname_safe, proj_info); Error sln_error = solution.save(); diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp index 126178125f..590ec007ad 100644 --- a/modules/mono/editor/godotsharp_export.cpp +++ b/modules/mono/editor/godotsharp_export.cpp @@ -106,13 +106,14 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug Map<String, String> dependencies; String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name"); - if (project_dll_name.empty()) { - project_dll_name = "UnnamedProject"; + String project_dll_name_safe = OS::get_singleton()->get_safe_dir_name(project_dll_name); + if (project_dll_name_safe.empty()) { + project_dll_name_safe = "UnnamedProject"; } String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config); - String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll"); - dependencies.insert(project_dll_name, project_dll_src_path); + String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name_safe + ".dll"); + dependencies.insert(project_dll_name_safe, project_dll_src_path); { MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain"); @@ -122,10 +123,10 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug _GDMONO_SCOPE_DOMAIN_(export_domain); GDMonoAssembly *scripts_assembly = NULL; - bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name, + bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name_safe, project_dll_src_path, &scripts_assembly, /* refonly: */ true); - ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name); + ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name_safe); ERR_FAIL_COND(!load_success); Vector<String> search_dirs; diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp index 47239f1260..e67c8b9ad9 100644 --- a/modules/mono/glue/collections_glue.cpp +++ b/modules/mono/glue/collections_glue.cpp @@ -46,7 +46,7 @@ void godot_icall_Array_Dtor(Array *ptr) { } MonoObject *godot_icall_Array_At(Array *ptr, int index) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return NULL; } @@ -54,7 +54,7 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) { } MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return NULL; } @@ -62,7 +62,7 @@ MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_en } void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; } @@ -124,7 +124,7 @@ MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) { } void godot_icall_Array_RemoveAt(Array *ptr, int index) { - if (index < 0 || index > ptr->size()) { + if (index < 0 || index >= ptr->size()) { GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range()); return; } diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 09a1fc6fbc..6445742899 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -39,6 +39,10 @@ #include "editor/editor_settings.h" #endif +#ifdef __ANDROID__ +#include "utils/android_utils.h" +#endif + namespace GodotSharpDirs { String _get_expected_build_config() { @@ -129,15 +133,16 @@ private: mono_solutions_dir = mono_user_dir.plus_file("solutions"); build_logs_dir = mono_user_dir.plus_file("build_logs"); - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; } String base_path = ProjectSettings::get_singleton()->globalize_path("res://"); - sln_filepath = base_path.plus_file(name + ".sln"); - csproj_filepath = base_path.plus_file(name + ".csproj"); + sln_filepath = base_path.plus_file(appname_safe + ".sln"); + csproj_filepath = base_path.plus_file(appname_safe + ".csproj"); #endif String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir(); @@ -150,7 +155,12 @@ private: String data_mono_root_dir = data_dir_root.plus_file("Mono"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); + +#if __ANDROID__ + data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir(); +#else data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); +#endif #ifdef WINDOWS_ENABLED data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); @@ -173,15 +183,21 @@ private: #else - String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); - String data_dir_root = exe_dir.plus_file("data_" + appname); + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + String data_dir_root = exe_dir.plus_file("data_" + appname_safe); if (!DirAccess::exists(data_dir_root)) { data_dir_root = exe_dir.plus_file("data_Godot"); } String data_mono_root_dir = data_dir_root.plus_file("Mono"); data_mono_etc_dir = data_mono_root_dir.plus_file("etc"); + +#if __ANDROID__ + data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir(); +#else data_mono_lib_dir = data_mono_root_dir.plus_file("lib"); +#endif #ifdef WINDOWS_ENABLED data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 7699e0d0cd..c90da31f3a 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -56,6 +56,10 @@ #include "main/main.h" #endif +#ifdef ANDROID_ENABLED +#include "android_mono_config.gen.h" +#endif + #define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. " \ "This error is expected if you just upgraded to a newer Godot version. " \ "Building the project will update the assembly to the correct version." @@ -287,7 +291,11 @@ void GDMono::initialize() { gdmono_debug_init(); #endif +#ifdef ANDROID_ENABLED + mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data()); +#else mono_config_parse(NULL); +#endif mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL); @@ -651,12 +659,13 @@ bool GDMono::_load_project_assembly() { if (project_assembly) return true; - String name = ProjectSettings::get_singleton()->get("application/config/name"); - if (name.empty()) { - name = "UnnamedProject"; + String appname = ProjectSettings::get_singleton()->get("application/config/name"); + String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + if (appname_safe.empty()) { + appname_safe = "UnnamedProject"; } - bool success = load_assembly(name, &project_assembly); + bool success = load_assembly(appname_safe, &project_assembly); if (success) { mono_assembly_set_main(project_assembly->get_assembly()); diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp index f1f0015ac9..1b765a09ff 100644 --- a/modules/mono/mono_gd/gd_mono_assembly.cpp +++ b/modules/mono/mono_gd/gd_mono_assembly.cpp @@ -270,7 +270,18 @@ Error GDMonoAssembly::load(bool p_refonly) { Vector<uint8_t> data = FileAccess::get_file_as_array(path); ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ); - String image_filename = ProjectSettings::get_singleton()->globalize_path(path); + String image_filename; + +#ifdef ANDROID_ENABLED + if (path.begins_with("res://")) { + image_filename = path.substr(6, path.length()); + } else { + image_filename = ProjectSettings::get_singleton()->globalize_path(path); + } +#else + // FIXME: globalize_path does not work on exported games + image_filename = ProjectSettings::get_singleton()->globalize_path(path); +#endif MonoImageOpenStatus status = MONO_IMAGE_OK; diff --git a/modules/mono/utils/android_utils.cpp b/modules/mono/utils/android_utils.cpp new file mode 100644 index 0000000000..7dd67e3b8e --- /dev/null +++ b/modules/mono/utils/android_utils.cpp @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* android_utils.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "android_utils.h" + +#ifdef __ANDROID__ + +#include "platform/android/thread_jandroid.h" + +namespace GDMonoUtils { +namespace Android { + +String get_app_native_lib_dir() { + JNIEnv *env = ThreadAndroid::get_env(); + + jclass activityThreadClass = env->FindClass("android/app/ActivityThread"); + jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;"); + jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread); + jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;"); + jobject ctx = env->CallObjectMethod(activityThread, getApplication); + + jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;"); + jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo); + jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;"); + jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField); + + String result; + + const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL); + if (nativeLibraryDir_utf8) { + result.parse_utf8(nativeLibraryDir_utf8); + env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8); + } + + return result; +} + +} // namespace Android +} // namespace GDMonoUtils + +#endif // __ANDROID__ diff --git a/modules/mono/utils/android_utils.h b/modules/mono/utils/android_utils.h new file mode 100644 index 0000000000..f911c3fdfe --- /dev/null +++ b/modules/mono/utils/android_utils.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* android_utils.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANDROID_UTILS_H +#define ANDROID_UTILS_H + +#ifdef __ANDROID__ + +#include "core/ustring.h" + +namespace GDMonoUtils { +namespace Android { + +String get_app_native_lib_dir(); + +} // namespace Android +} // namespace GDMonoUtils + +#endif // __ANDROID__ + +#endif // ANDROID_UTILS_H diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 899abfc9f9..bd5ce91e77 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -731,6 +731,8 @@ void TileSet::tile_set_shape(int p_id, int p_shape_id, const Ref<Shape2D> &p_sha Ref<Shape2D> TileSet::tile_get_shape(int p_id, int p_shape_id) const { ERR_FAIL_COND_V(!tile_map.has(p_id), Ref<Shape2D>()); + ERR_FAIL_COND_V(p_shape_id < 0, Ref<Shape2D>()); + if (p_shape_id < tile_map[p_id].shapes_data.size()) return tile_map[p_id].shapes_data[p_shape_id].shape; @@ -740,6 +742,7 @@ Ref<Shape2D> TileSet::tile_get_shape(int p_id, int p_shape_id) const { void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform2D &p_offset) { ERR_FAIL_COND(!tile_map.has(p_id)); + ERR_FAIL_COND(p_shape_id < 0); if (p_shape_id >= tile_map[p_id].shapes_data.size()) tile_map[p_id].shapes_data.resize(p_shape_id + 1); @@ -750,6 +753,8 @@ void TileSet::tile_set_shape_transform(int p_id, int p_shape_id, const Transform Transform2D TileSet::tile_get_shape_transform(int p_id, int p_shape_id) const { ERR_FAIL_COND_V(!tile_map.has(p_id), Transform2D()); + ERR_FAIL_COND_V(p_shape_id < 0, Transform2D()); + if (p_shape_id < tile_map[p_id].shapes_data.size()) return tile_map[p_id].shapes_data[p_shape_id].shape_transform; @@ -780,6 +785,8 @@ void TileSet::tile_set_shape_one_way(int p_id, int p_shape_id, const bool p_one_ bool TileSet::tile_get_shape_one_way(int p_id, int p_shape_id) const { ERR_FAIL_COND_V(!tile_map.has(p_id), false); + ERR_FAIL_COND_V(p_shape_id < 0, false); + if (p_shape_id < tile_map[p_id].shapes_data.size()) return tile_map[p_id].shapes_data[p_shape_id].one_way_collision; @@ -800,6 +807,8 @@ void TileSet::tile_set_shape_one_way_margin(int p_id, int p_shape_id, float p_ma float TileSet::tile_get_shape_one_way_margin(int p_id, int p_shape_id) const { ERR_FAIL_COND_V(!tile_map.has(p_id), 0); + ERR_FAIL_COND_V(p_shape_id < 0, 0); + if (p_shape_id < tile_map[p_id].shapes_data.size()) return tile_map[p_id].shapes_data[p_shape_id].one_way_collision_margin; |