feat: modules moved and engine moved to submodule

This commit is contained in:
Jan van der Weide 2025-04-12 18:40:44 +02:00
parent dfb5e645cd
commit c33d2130cc
5136 changed files with 225275 additions and 64485 deletions

View file

@ -5,7 +5,6 @@ Import("env")
env.editor_sources = []
import glob
import os
import editor_builders
@ -17,17 +16,16 @@ if env.editor_build:
def doc_data_class_path_builder(target, source, env):
paths = dict(sorted(source[0].read().items()))
data = "\n".join([f'\t{{"{key}", "{value}"}},' for key, value in paths.items()])
with methods.generated_wrapper(target) as file:
with methods.generated_wrapper(str(target[0])) as file:
file.write(
f"""\
static const int _doc_data_class_path_count = {len(paths)};
struct _DocDataClassPath {{
const char *name;
const char *path;
}};
static const _DocDataClassPath _doc_data_class_paths[{len(env.doc_class_path) + 1}] = {{
inline constexpr int _doc_data_class_path_count = {len(paths)};
inline constexpr _DocDataClassPath _doc_data_class_paths[{len(env.doc_class_path) + 1}] = {{
{data}
{{nullptr, nullptr}},
}};
@ -42,7 +40,7 @@ static const _DocDataClassPath _doc_data_class_paths[{len(env.doc_class_path) +
exp_inc = "\n".join([f'#include "platform/{p}/export/export.h"' for p in platforms])
exp_reg = "\n".join([f"\tregister_{p}_exporter();" for p in platforms])
exp_type = "\n".join([f"\tregister_{p}_exporter_types();" for p in platforms])
with methods.generated_wrapper(target) as file:
with methods.generated_wrapper(str(target[0])) as file:
file.write(
f"""\
#include "register_exporters.h"
@ -83,7 +81,6 @@ void register_exporter_types() {{
docs += Glob(d + "/*.xml") # Custom.
docs = sorted(docs)
env.Depends("#editor/doc_data_compressed.gen.h", docs)
env.CommandNoCache(
"#editor/doc_data_compressed.gen.h",
docs,
@ -97,40 +94,31 @@ void register_exporter_types() {{
# Generated with `make include-list` for each resource.
# Editor translations
tlist = glob.glob(env.Dir("#editor/translations/editor").abspath + "/*.po")
env.Depends("#editor/editor_translations.gen.h", tlist)
env.CommandNoCache(
"#editor/editor_translations.gen.h",
tlist,
env.Run(editor_builders.make_editor_translations_header),
Glob("#editor/translations/editor/*"),
env.Run(editor_builders.make_translations_header),
)
# Property translations
tlist = glob.glob(env.Dir("#editor/translations/properties").abspath + "/*.po")
env.Depends("#editor/property_translations.gen.h", tlist)
env.CommandNoCache(
"#editor/property_translations.gen.h",
tlist,
env.Run(editor_builders.make_property_translations_header),
Glob("#editor/translations/properties/*"),
env.Run(editor_builders.make_translations_header),
)
# Documentation translations
tlist = glob.glob(env.Dir("#doc/translations").abspath + "/*.po")
env.Depends("#editor/doc_translations.gen.h", tlist)
env.CommandNoCache(
"#editor/doc_translations.gen.h",
tlist,
env.Run(editor_builders.make_doc_translations_header),
Glob("#doc/translations/*"),
env.Run(editor_builders.make_translations_header),
)
# Extractable translations
tlist = glob.glob(env.Dir("#editor/translations/extractable").abspath + "/*.po")
tlist.extend(glob.glob(env.Dir("#editor/translations/extractable").abspath + "/extractable.pot"))
env.Depends("#editor/extractable_translations.gen.h", tlist)
env.CommandNoCache(
"#editor/extractable_translations.gen.h",
tlist,
env.Run(editor_builders.make_extractable_translations_header),
Glob("#editor/translations/extractable/*"),
env.Run(editor_builders.make_translations_header),
)
env.add_source_files(env.editor_sources, "*.cpp")

View file

@ -257,6 +257,8 @@ Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
action_tree->set_drag_preview(label);
get_viewport()->gui_set_drag_description(vformat(RTR("Action %s"), name));
Dictionary drag_data;
if (selected->has_meta("__action")) {
@ -267,6 +269,8 @@ Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from
drag_data["input_type"] = "event";
}
drag_data["source"] = selected->get_instance_id();
action_tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN);
return drag_data;
@ -278,9 +282,11 @@ bool ActionMapEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_d
return false;
}
TreeItem *source = Object::cast_to<TreeItem>(ObjectDB::get_instance(d["source"].operator ObjectID()));
TreeItem *selected = action_tree->get_selected();
TreeItem *item = action_tree->get_item_at_position(p_point);
if (!selected || !item || item == selected) {
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? selected : action_tree->get_item_at_position(p_point);
if (!selected || !item || item == source) {
return false;
}
@ -303,13 +309,13 @@ void ActionMapEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data,
}
TreeItem *selected = action_tree->get_selected();
TreeItem *target = action_tree->get_item_at_position(p_point);
bool drop_above = action_tree->get_drop_section_at_position(p_point) == -1;
TreeItem *target = (p_point == Vector2(Math::INF, Math::INF)) ? selected : action_tree->get_item_at_position(p_point);
if (!target) {
return;
}
bool drop_above = ((p_point == Vector2(Math::INF, Math::INF)) ? action_tree->get_drop_section_at_position(action_tree->get_item_rect(target).position) : action_tree->get_drop_section_at_position(p_point)) == -1;
Dictionary d = p_data;
if (d["input_type"] == "action") {
// Change action order.
@ -501,8 +507,8 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info
}
// Third Column - Buttons
event_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Edit")), BUTTON_EDIT_EVENT, false, TTR("Edit Event"));
event_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE_EVENT, false, TTR("Remove Event"));
event_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Edit")), BUTTON_EDIT_EVENT, false, TTR("Edit Event"), TTR("Edit Event"));
event_item->add_button(2, action_tree->get_editor_theme_icon(SNAME("Remove")), BUTTON_REMOVE_EVENT, false, TTR("Remove Event"), TTR("Remove Event"));
event_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
event_item->set_button_color(2, 1, Color(1, 1, 1, 0.75));
}
@ -543,6 +549,7 @@ ActionMapEditor::ActionMapEditor() {
action_list_search = memnew(LineEdit);
action_list_search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_list_search->set_placeholder(TTR("Filter by Name"));
action_list_search->set_accessibility_name(TTRC("Filter by Name"));
action_list_search->set_clear_button_enabled(true);
action_list_search->connect(SceneStringName(text_changed), callable_mp(this, &ActionMapEditor::_search_term_updated));
top_hbox->add_child(action_list_search);
@ -550,6 +557,7 @@ ActionMapEditor::ActionMapEditor() {
action_list_search_by_event = memnew(EventListenerLineEdit);
action_list_search_by_event->set_h_size_flags(Control::SIZE_EXPAND_FILL);
action_list_search_by_event->set_stretch_ratio(0.75);
action_list_search_by_event->set_accessibility_name(TTRC("Action Event"));
action_list_search_by_event->connect("event_changed", callable_mp(this, &ActionMapEditor::_search_by_event));
action_list_search_by_event->connect(SceneStringName(focus_entered), callable_mp(this, &ActionMapEditor::_on_filter_focused));
action_list_search_by_event->connect(SceneStringName(focus_exited), callable_mp(this, &ActionMapEditor::_on_filter_unfocused));
@ -569,6 +577,7 @@ ActionMapEditor::ActionMapEditor() {
add_edit = memnew(LineEdit);
add_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
add_edit->set_placeholder(TTR("Add New Action"));
add_edit->set_accessibility_name(TTRC("Add New Action"));
add_edit->set_clear_button_enabled(true);
add_edit->set_keep_editing_on_text_submit(true);
add_edit->connect(SceneStringName(text_changed), callable_mp(this, &ActionMapEditor::_add_edit_text_changed));
@ -597,6 +606,7 @@ ActionMapEditor::ActionMapEditor() {
// Action Editor Tree
action_tree = memnew(Tree);
action_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
action_tree->set_accessibility_name(TTRC("Action Map"));
action_tree->set_columns(3);
action_tree->set_hide_root(true);
action_tree->set_column_titles_visible(true);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ACTION_MAP_EDITOR_H
#define ACTION_MAP_EDITOR_H
#pragma once
#include "scene/gui/control.h"
@ -132,5 +131,3 @@ public:
ActionMapEditor();
};
#endif // ACTION_MAP_EDITOR_H

View file

@ -41,11 +41,13 @@ AddMetadataDialog::AddMetadataDialog() {
hbc->add_child(memnew(Label(TTR("Name:"))));
add_meta_name = memnew(LineEdit);
add_meta_name->set_accessibility_name(TTRC("Name:"));
add_meta_name->set_custom_minimum_size(Size2(200 * EDSCALE, 1));
hbc->add_child(add_meta_name);
hbc->add_child(memnew(Label(TTR("Type:"))));
add_meta_type = memnew(OptionButton);
add_meta_type->set_accessibility_name(TTRC("Type:"));
hbc->add_child(add_meta_type);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ADD_METADATA_DIALOG_H
#define ADD_METADATA_DIALOG_H
#pragma once
#include "editor/gui/editor_validation_panel.h"
#include "scene/gui/dialogs.h"
@ -56,4 +55,3 @@ private:
OptionButton *add_meta_type = nullptr;
EditorValidationPanel *validation_panel = nullptr;
};
#endif // ADD_METADATA_DIALOG_H

View file

@ -60,8 +60,12 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
for (int i = 0; i < animation->track_get_key_count(p_track); i++) {
real_t ofs = animation->track_get_key_time(p_track, i);
if (moving_selection && selection.has(IntPair(p_track, i))) {
ofs += moving_selection_offset.x;
if (selection.has(IntPair(p_track, i))) {
if (moving_selection) {
ofs += moving_selection_offset.x;
} else if (scaling_selection) {
ofs += -scaling_selection_offset.x + (ofs - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
}
}
key_order[ofs] = i;
@ -83,9 +87,14 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
out_handle = moving_handle_right;
}
if (moving_selection && selection.has(IntPair(p_track, i))) {
offset += moving_selection_offset.x;
height += moving_selection_offset.y;
if (selection.has(IntPair(p_track, i))) {
if (moving_selection) {
offset += moving_selection_offset.x;
height += moving_selection_offset.y;
} else if (scaling_selection) {
offset += -scaling_selection_offset.x + (offset - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
height += -scaling_selection_offset.y + (height - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
}
}
out_handle += Vector2(offset, height);
@ -97,9 +106,14 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
in_handle = moving_handle_left;
}
if (moving_selection && selection.has(IntPair(p_track, i_n))) {
offset_n += moving_selection_offset.x;
height_n += moving_selection_offset.y;
if (selection.has(IntPair(p_track, i_n))) {
if (moving_selection) {
offset_n += moving_selection_offset.x;
height_n += moving_selection_offset.y;
} else if (scaling_selection) {
offset_n += -scaling_selection_offset.x + (offset_n - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
height_n += -scaling_selection_offset.y + (height_n - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
}
}
in_handle += Vector2(offset_n, height_n);
@ -231,6 +245,15 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
selected_icon = get_editor_theme_icon(SNAME("KeyBezierSelected"));
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
//TODO
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Animation bezier track editor")));
} break;
case NOTIFICATION_DRAW: {
if (animation.is_null()) {
return;
@ -506,29 +529,46 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}
const bool draw_selection_handles = selection.size() > 1;
LocalVector<Point2> selected_pos;
// Draw editor handles.
{
edit_points.clear();
float scale = timeline->get_zoom_scale();
for (int i = 0; i < track_count; ++i) {
if (!_is_track_curves_displayed(i) || locked_tracks.has(i)) {
bool draw_track = _is_track_curves_displayed(i) && !locked_tracks.has(i);
if (!draw_selection_handles && !draw_track) {
continue;
}
int key_count = animation->track_get_key_count(i);
for (int j = 0; j < key_count; ++j) {
float offset = animation->track_get_key_time(i, j);
float value = animation->bezier_track_get_key_value(i, j);
bool is_selected = selection.has(IntPair(i, j));
if (moving_selection && selection.has(IntPair(i, j))) {
offset += moving_selection_offset.x;
value += moving_selection_offset.y;
if (is_selected) {
if (moving_selection) {
offset += moving_selection_offset.x;
value += moving_selection_offset.y;
} else if (scaling_selection) {
offset += -scaling_selection_offset.x + (offset - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
value += -scaling_selection_offset.y + (value - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
}
}
Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
if (draw_selection_handles && is_selected) {
selected_pos.push_back(pos);
}
if (!draw_track) {
continue;
}
Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) {
@ -544,7 +584,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y));
if (i == selected_track || selection.has(IntPair(i, j))) {
if (i == selected_track || is_selected) {
_draw_line_clipped(pos, pos_in, accent, limit, right_limit);
_draw_line_clipped(pos, pos_out, accent, limit, right_limit);
}
@ -555,7 +595,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
if (pos.x >= limit && pos.x <= right_limit) {
ep.point_rect.position = (pos - bezier_icon->get_size() / 2.0).floor();
ep.point_rect.size = bezier_icon->get_size();
if (selection.has(IntPair(i, j))) {
if (is_selected) {
draw_texture(selected_icon, ep.point_rect.position);
draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.0001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
@ -570,7 +610,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
if (i == selected_track || selection.has(IntPair(i, j))) {
if (i == selected_track || is_selected) {
if (animation->bezier_track_get_key_handle_mode(i, j) != Animation::HANDLE_MODE_LINEAR) {
if (pos_in.x >= limit && pos_in.x <= right_limit) {
ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2.0).floor();
@ -601,6 +641,34 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}
selection_rect = Rect2();
selection_handles_rect = Rect2();
// Draw scale handles.
if (draw_selection_handles) {
selection_rect.position = selected_pos[0];
selected_pos.remove_at(0);
for (const Point2 &pos : selected_pos) {
selection_rect = selection_rect.expand(pos);
}
const int outer_ofs = Math::round(12 * EDSCALE);
const int inner_ofs = Math::round(outer_ofs / 2.0);
// Draw horizontal handles.
if (selection_rect.size.height > CMP_EPSILON) {
_draw_line_clipped(selection_rect.position - Vector2(inner_ofs, inner_ofs), selection_rect.position + Vector2(selection_rect.size.width + inner_ofs, -inner_ofs), accent, limit, right_limit);
_draw_line_clipped(selection_rect.position + Vector2(-inner_ofs, selection_rect.size.height + inner_ofs), selection_rect.position + selection_rect.size + Vector2(inner_ofs, inner_ofs), accent, limit, right_limit);
}
// Draw vertical handles.
if (selection_rect.size.width > CMP_EPSILON) {
_draw_line_clipped(selection_rect.position - Vector2(inner_ofs, inner_ofs), selection_rect.position + Vector2(-inner_ofs, selection_rect.size.height + inner_ofs), accent, limit, right_limit);
_draw_line_clipped(selection_rect.position + Vector2(selection_rect.size.width + inner_ofs, -inner_ofs), selection_rect.position + selection_rect.size + Vector2(inner_ofs, inner_ofs), accent, limit, right_limit);
}
selection_handles_rect.position = selection_rect.position - Vector2(outer_ofs, outer_ofs);
selection_handles_rect.size = selection_rect.size + Vector2(outer_ofs, outer_ofs) * 2;
}
if (box_selecting) {
Vector2 bs_from = box_selection_from;
Vector2 bs_to = box_selection_to;
@ -756,8 +824,8 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
void AnimationBezierTrackEdit::auto_fit_vertically() {
int track_count = animation->get_track_count();
real_t minimum_value = INFINITY;
real_t maximum_value = -INFINITY;
real_t minimum_value = Math::INF;
real_t maximum_value = -Math::INF;
int nb_track_visible = 0;
for (int i = 0; i < track_count; ++i) {
@ -968,10 +1036,10 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
real_t minimum_time = INFINITY;
real_t maximum_time = -INFINITY;
real_t minimum_value = INFINITY;
real_t maximum_value = -INFINITY;
real_t minimum_time = Math::INF;
real_t maximum_time = -Math::INF;
real_t minimum_value = Math::INF;
real_t maximum_value = -Math::INF;
for (const IntPair &E : focused_keys) {
IntPair key_pair = E;
@ -1193,15 +1261,17 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
// Check this first, to allow manipulating key handles while ignoring keyframes before scaling/moving.
bool inside_selection_handles_rect = !read_only && selection_handles_rect.has_point(mb->get_position());
// First, check keyframe.
// Command/Control makes it ignore the keyframe, so control point editors can be force-edited.
if (!mb->is_command_or_control_pressed()) {
if (!inside_selection_handles_rect && !mb->is_command_or_control_pressed()) {
if (_try_select_at_ui_pos(mb->get_position(), mb->is_shift_pressed(), true)) {
return;
}
}
// Second, check handles.
// Second, check key handles.
for (int i = 0; i < edit_points.size(); i++) {
if (!read_only) {
if (edit_points[i].in_rect.has_point(mb->get_position())) {
@ -1226,6 +1296,50 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
// Box scaling/movement.
if (inside_selection_handles_rect) {
const Vector2i rel_pos = mb->get_position() - selection_rect.position;
scaling_selection_handles = Vector2i();
// Check which scaling handles are available.
if (selection_rect.size.width > CMP_EPSILON) {
if (rel_pos.x <= 0) {
scaling_selection_handles.x = -1;
} else if (rel_pos.x >= selection_rect.size.width) {
scaling_selection_handles.x = 1;
}
}
if (selection_rect.size.height > CMP_EPSILON) {
if (rel_pos.y <= 0) {
scaling_selection_handles.y = -1;
} else if (rel_pos.y >= selection_rect.size.height) {
scaling_selection_handles.y = 1;
}
}
if (scaling_selection_handles != Vector2i()) {
scaling_selection = true;
const float time = ((selection_rect.position.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
const float h = (get_size().height / 2.0 - selection_rect.position.y) * timeline_v_zoom + timeline_v_scroll;
scaling_selection_pivot = Point2(time, h);
return;
}
// If not scaling, that means we're moving.
moving_selection_attempt = true;
moving_selection = false;
moving_selection_mouse_begin = mb->get_position();
// The pivot will be from the mouse click location, not a specific key.
moving_selection_from_key = -1;
moving_selection_from_track = selected_track;
moving_selection_offset = Vector2();
select_single_attempt = IntPair(-1, -1);
return;
}
// Insert new point.
if (mb->get_position().x >= limit && mb->get_position().x < get_size().width && mb->is_command_or_control_pressed()) {
float h = (get_size().height / 2.0 - mb->get_position().y) * timeline_v_zoom + timeline_v_scroll;
@ -1250,7 +1364,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
moving_selection_attempt = true;
moving_selection = false;
moving_selection_mouse_begin_x = mb->get_position().x;
moving_selection_mouse_begin = mb->get_position();
moving_selection_from_key = index;
moving_selection_from_track = selected_track;
moving_selection_offset = Vector2();
@ -1285,12 +1399,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (bs_from.y > bs_to.y) {
SWAP(bs_from.y, bs_to.y);
}
Rect2 selection_rect(bs_from, bs_to - bs_from);
Rect2 rect(bs_from, bs_to - bs_from);
bool track_set = false;
int j = 0;
for (int i = 0; i < edit_points.size(); i++) {
if (edit_points[i].point_rect.intersects(selection_rect)) {
if (edit_points[i].point_rect.intersects(rect)) {
_select_at_anim(animation, edit_points[i].track, animation->track_get_key_time(edit_points[i].track, edit_points[i].key), j == 0 && !box_selecting_add);
if (!track_set) {
track_set = true;
@ -1335,8 +1449,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (!read_only) {
if (moving_selection && (abs(moving_selection_offset.x) > CMP_EPSILON || abs(moving_selection_offset.y) > CMP_EPSILON)) {
//combit it
// Commit it.
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Bezier Points"));
@ -1459,11 +1572,141 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
moving_selection = false;
moving_selection_attempt = false;
moving_selection_mouse_begin_x = 0.0;
moving_selection_mouse_begin = Point2();
queue_redraw();
}
}
if (scaling_selection && mb.is_valid() && !read_only && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (abs(scaling_selection_scale.x - 1) > CMP_EPSILON || abs(scaling_selection_scale.y - 1) > CMP_EPSILON) {
// Scale it.
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Scale Bezier Points"));
List<AnimMoveRestore> to_restore;
List<Animation::HandleMode> to_restore_handle_modes;
// 1 - Remove the keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second);
}
// 2 - Remove overlapped keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t newtime = animation->track_get_key_time(E->get().first, E->get().second);
newtime += -scaling_selection_offset.x + (newtime - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
int idx = animation->track_find_key(E->get().first, newtime, Animation::FIND_MODE_APPROX);
if (idx == -1) {
continue;
}
if (selection.has(IntPair(E->get().first, idx))) {
continue; // Already in selection, don't save.
}
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
AnimMoveRestore amr;
amr.key = animation->track_get_key_value(E->get().first, idx);
amr.track = E->get().first;
amr.time = newtime;
to_restore.push_back(amr);
to_restore_handle_modes.push_back(animation->bezier_track_get_key_handle_mode(E->get().first, idx));
}
// 3 - Scale the keys (re-insert them).
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
Array key = animation->track_get_key_value(E->get().first, E->get().second);
real_t h = key[0];
h += -scaling_selection_offset.y + (h - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
key[0] = h;
undo_redo->add_do_method(
this,
"_bezier_track_insert_key_at_anim",
animation,
E->get().first,
newpos,
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
}
// 4 - (undo) Remove inserted keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
}
// 5 - (undo) Reinsert keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
Array key = animation->track_get_key_value(E->get().first, E->get().second);
undo_redo->add_undo_method(
this,
"_bezier_track_insert_key_at_anim",
animation,
E->get().first,
oldpos,
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
}
// 6 - (undo) Reinsert overlapped keys.
List<AnimMoveRestore>::ConstIterator restore_itr = to_restore.begin();
List<Animation::HandleMode>::ConstIterator handle_itr = to_restore_handle_modes.begin();
for (; restore_itr != to_restore.end() && handle_itr != to_restore_handle_modes.end(); ++restore_itr, ++handle_itr) {
const AnimMoveRestore &amr = *restore_itr;
Array key = amr.key;
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
undo_redo->add_undo_method(
this,
"_bezier_track_insert_key_at_anim",
animation,
amr.track,
amr.time,
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
*handle_itr);
}
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
// 7 - Reselect.
int i = 0;
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos, i == 0);
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos, i == 0);
i++;
}
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
if (ape) {
undo_redo->add_do_method(ape, "_animation_update_key_frame");
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
}
undo_redo->commit_action();
}
scaling_selection = false;
scaling_selection_scale = Vector2(1, 1);
scaling_selection_offset = Vector2();
queue_redraw();
}
Ref<InputEventMouseMotion> mm = p_event;
if (moving_selection_attempt && mm.is_valid()) {
if (!moving_selection) {
@ -1473,9 +1716,9 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (!read_only) {
float y = (get_size().height / 2.0 - mm->get_position().y) * timeline_v_zoom + timeline_v_scroll;
float moving_selection_begin_time = ((moving_selection_mouse_begin_x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
float moving_selection_begin_time = ((moving_selection_mouse_begin.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
float new_time = ((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
float moving_selection_pivot = animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key);
float moving_selection_pivot = moving_selection_from_key != -1 ? animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key) : 0;
float time_delta = new_time - moving_selection_begin_time;
float snapped_time = editor->snap_time(moving_selection_pivot + time_delta);
@ -1483,9 +1726,15 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (abs(moving_selection_offset.x) > CMP_EPSILON || (snapped_time > moving_selection_pivot && time_delta > CMP_EPSILON) || (snapped_time < moving_selection_pivot && time_delta < -CMP_EPSILON)) {
time_offset = snapped_time - moving_selection_pivot;
}
float moving_selection_begin_value = animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key);
float y_offset = y - moving_selection_begin_value;
float moving_selection_begin_value;
if (moving_selection_from_key == -1) {
moving_selection_begin_value = (get_size().height / 2.0 - moving_selection_mouse_begin.y) * timeline_v_zoom + timeline_v_scroll;
} else {
moving_selection_begin_value = animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key);
}
float y_offset = y - moving_selection_begin_value;
moving_selection_offset = Vector2(time_offset, y_offset);
}
@ -1505,11 +1754,82 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
queue_redraw();
}
if (scaling_selection && mm.is_valid() && !read_only) {
Point2 mp = mm->get_position();
const int handle_length = Math::round((selection_handles_rect.size.width - selection_rect.size.width) / 4.0);
Point2 rel_pos;
// Calculate the scale according with the distance between the mouse's position (adjusted so that the cursor appears inside the handles)
// and the opposite end of the `selection_rect`.
if (scaling_selection_handles.x != 0) {
if (scaling_selection_handles.x == 1) { // Right Handle
const int handle_adjust = Math::round(mp.x - (scaling_selection_scale.x >= 0 ? selection_rect.position.x : (selection_rect.position.x + selection_rect.size.width)));
mp.x -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.x * SIGN(handle_adjust);
if (editor->is_snap_keys_enabled()) {
mp.x = editor->snap_time((mp.x - limit) / timeline->get_zoom_scale(), true) + timeline->get_value();
mp.x = (mp.x - timeline->get_value()) * timeline->get_zoom_scale() + limit;
}
rel_pos.x = scaling_selection_scale.x >= 0 ? (mp.x - selection_rect.position.x) : selection_rect.position.x + selection_rect.size.width - mp.x;
} else { // Left Handle
const int handle_adjust = Math::round((scaling_selection_scale.x >= 0 ? (selection_rect.position.x + selection_rect.size.width) : selection_rect.position.x) - mp.x);
mp.x -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.x * SIGN(handle_adjust);
const float x = editor->snap_time((mp.x - limit) / timeline->get_zoom_scale(), true) + timeline->get_value();
if (editor->is_snap_keys_enabled()) {
mp.x = (x - timeline->get_value()) * timeline->get_zoom_scale() + limit;
}
rel_pos.x = scaling_selection_scale.x >= 0 ? (selection_rect.position.x + selection_rect.size.width - mp.x) : (mp.x - selection_rect.position.x);
scaling_selection_offset.x = scaling_selection_pivot.x - x;
}
scaling_selection_scale.x *= rel_pos.x / selection_rect.size.width;
if (scaling_selection_scale.x == 0) {
scaling_selection_scale.x = CMP_EPSILON;
}
}
if (scaling_selection_handles.y != 0) {
if (scaling_selection_handles.y == 1) { // Bottom Handle
const int handle_adjust = Math::round(mp.y - (scaling_selection_scale.y >= 0 ? selection_rect.position.y : (selection_rect.position.y + selection_rect.size.height)));
mp.y -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.y * SIGN(handle_adjust);
if (scaling_selection_scale.y >= 0) {
rel_pos.y = mp.y - selection_rect.position.y;
} else {
rel_pos.y = selection_rect.position.y + selection_rect.size.height - mp.y;
}
} else { // Top Handle
const int handle_adjust = Math::round((scaling_selection_scale.y >= 0 ? (selection_rect.position.y + selection_rect.size.height) : selection_rect.position.y) - mp.y);
mp.y -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.y * SIGN(handle_adjust);
if (scaling_selection_scale.y >= 0) {
rel_pos.y = selection_rect.position.y + selection_rect.size.height - mp.y;
} else {
rel_pos.y = mp.y - selection_rect.position.y;
}
const float h = (get_size().height / 2.0 - mp.y) * timeline_v_zoom + timeline_v_scroll;
scaling_selection_offset.y = scaling_selection_pivot.y - h;
}
scaling_selection_scale.y *= rel_pos.y / selection_rect.size.height;
if (scaling_selection_scale.y == 0) {
scaling_selection_scale.y = CMP_EPSILON;
}
}
queue_redraw();
}
if ((moving_handle == 1 || moving_handle == -1) && mm.is_valid()) {
float y = (get_size().height / 2.0 - mm->get_position().y) * timeline_v_zoom + timeline_v_scroll;
float x = editor->snap_time((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
float x = editor->snap_time((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
Vector2 key_pos = Vector2(animation->track_get_key_time(selected_track, moving_handle_key), animation->bezier_track_get_key_value(selected_track, moving_handle_key));
Vector2 key_pos = Vector2(animation->track_get_key_time(moving_handle_track, moving_handle_key), animation->bezier_track_get_key_value(moving_handle_track, moving_handle_key));
Vector2 moving_handle_value = Vector2(x, y) - key_pos;
@ -1601,7 +1921,7 @@ bool AnimationBezierTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p
moving_selection_attempt = true;
moving_selection_from_key = pair.second;
moving_selection_from_track = pair.first;
moving_selection_mouse_begin_x = p_pos.x;
moving_selection_mouse_begin = p_pos;
moving_selection_offset = Vector2();
moving_handle_track = pair.first;
moving_handle_left = animation->bezier_track_get_key_in_handle(pair.first, pair.second);
@ -1731,7 +2051,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
}
void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_valid) {
if (selection.size() == 0) {
if (selection.is_empty()) {
return;
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ANIMATION_BEZIER_EDITOR_H
#define ANIMATION_BEZIER_EDITOR_H
#pragma once
#include "animation_track_editor.h"
#include "core/templates/hashfuncs.h"
@ -109,7 +108,7 @@ class AnimationBezierTrackEdit : public Control {
typedef Pair<int, int> IntPair;
bool moving_selection_attempt = false;
float moving_selection_mouse_begin_x = 0.0;
Point2 moving_selection_mouse_begin;
IntPair select_single_attempt;
bool moving_selection = false;
int moving_selection_from_key = 0;
@ -123,6 +122,15 @@ class AnimationBezierTrackEdit : public Control {
Vector2 box_selection_from;
Vector2 box_selection_to;
Rect2 selection_rect;
Rect2 selection_handles_rect;
bool scaling_selection = false;
Vector2i scaling_selection_handles;
Vector2 scaling_selection_scale = Vector2(1, 1);
Vector2 scaling_selection_offset;
Point2 scaling_selection_pivot;
int moving_handle = 0; //0 no move -1 or +1 out, 2 both (drawing only)
int moving_handle_key = 0;
int moving_handle_track = 0;
@ -225,5 +233,3 @@ public:
AnimationBezierTrackEdit();
};
#endif // ANIMATION_BEZIER_EDITOR_H

View file

@ -221,10 +221,10 @@ bool AnimationTrackKeyEdit::_set(const StringName &p_name, const Variant &p_valu
change_notify_deserved = true;
} else if (name.begins_with("args/")) {
Vector<Variant> args = d_old["args"];
int idx = name.get_slice("/", 1).to_int();
int idx = name.get_slicec('/', 1).to_int();
ERR_FAIL_INDEX_V(idx, args.size(), false);
String what = name.get_slice("/", 2);
String what = name.get_slicec('/', 2);
if (what == "type") {
Variant::Type t = Variant::Type(int(p_value));
@ -478,10 +478,10 @@ bool AnimationTrackKeyEdit::_get(const StringName &p_name, Variant &r_ret) const
}
if (name.begins_with("args/")) {
int idx = name.get_slice("/", 1).to_int();
int idx = name.get_slicec('/', 1).to_int();
ERR_FAIL_INDEX_V(idx, args.size(), false);
String what = name.get_slice("/", 2);
String what = name.get_slicec('/', 2);
if (what == "type") {
r_ret = args[idx].get_type();
return true;
@ -833,10 +833,10 @@ bool AnimationMultiTrackKeyEdit::_set(const StringName &p_name, const Variant &p
change_notify_deserved = true;
} else if (name.begins_with("args/")) {
Vector<Variant> args = d_old["args"];
int idx = name.get_slice("/", 1).to_int();
int idx = name.get_slicec('/', 1).to_int();
ERR_FAIL_INDEX_V(idx, args.size(), false);
String what = name.get_slice("/", 2);
String what = name.get_slicec('/', 2);
if (what == "type") {
Variant::Type t = Variant::Type(int(p_value));
@ -1055,10 +1055,10 @@ bool AnimationMultiTrackKeyEdit::_get(const StringName &p_name, Variant &r_ret)
}
if (name.begins_with("args/")) {
int idx = name.get_slice("/", 1).to_int();
int idx = name.get_slicec('/', 1).to_int();
ERR_FAIL_INDEX_V(idx, args.size(), false);
String what = name.get_slice("/", 2);
String what = name.get_slicec('/', 2);
if (what == "type") {
r_ret = args[idx].get_type();
return true;
@ -1251,12 +1251,12 @@ void AnimationMultiTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list)
if (ap) {
List<StringName> anims;
ap->get_animation_list(&anims);
for (List<StringName>::Element *G = anims.front(); G; G = G->next()) {
for (const StringName &anim : anims) {
if (!animations.is_empty()) {
animations += ",";
}
animations += String(G->get());
animations += String(anim);
}
}
}
@ -1485,6 +1485,15 @@ void AnimationTimelineEdit::_notification(int p_what) {
len_hb->set_size(Size2(get_buttons_width(), get_size().height));
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
//TODO
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Animation timeline editor")));
} break;
case NOTIFICATION_DRAW: {
int key_range = get_size().width - get_buttons_width() - get_name_limit();
@ -2008,10 +2017,12 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
expander->set_h_size_flags(SIZE_EXPAND_FILL);
expander->set_mouse_filter(MOUSE_FILTER_IGNORE);
len_hb->add_child(expander);
time_icon = memnew(TextureRect);
time_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
time_icon->set_tooltip_text(TTR("Animation length (seconds)"));
len_hb->add_child(time_icon);
length = memnew(EditorSpinSlider);
length->set_min(SECOND_DECIMAL);
length->set_max(36000);
@ -2020,11 +2031,14 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0));
length->set_hide_slider(true);
length->set_tooltip_text(TTR("Animation length (seconds)"));
length->set_accessibility_name(TTRC("Animation length"));
length->connect(SceneStringName(value_changed), callable_mp(this, &AnimationTimelineEdit::_anim_length_changed));
len_hb->add_child(length);
loop = memnew(Button);
loop->set_flat(true);
loop->set_tooltip_text(TTR("Animation Looping"));
loop->set_accessibility_name(TTRC("Animation Looping"));
loop->connect(SceneStringName(pressed), callable_mp(this, &AnimationTimelineEdit::_anim_loop_pressed));
loop->set_toggle_mode(true);
len_hb->add_child(loop);
@ -2056,6 +2070,15 @@ void AnimationTrackEdit::_notification(int p_what) {
selected_icon = get_editor_theme_icon(SNAME("KeySelected"));
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
//TODO
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Animation track editor")));
} break;
case NOTIFICATION_DRAW: {
if (animation.is_null()) {
return;
@ -2235,8 +2258,21 @@ void AnimationTrackEdit::_notification(int p_what) {
offset_n = offset_n + editor->get_moving_selection_offset();
}
offset_n = offset_n * scale + limit;
float offset_last = limit_end;
if (i < animation->track_get_key_count(track) - 2) {
offset_last = animation->track_get_key_time(track, i + 2) - timeline->get_value();
if (editor->is_key_selected(track, i + 2) && editor->is_moving_selection()) {
offset_last = offset_last + editor->get_moving_selection_offset();
}
offset_last = offset_last * scale + limit;
}
int limit_string = (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) ? int(offset_last) : int(offset_n);
if (editor->is_key_selected(track, i) && editor->is_moving_selection()) {
limit_string = int(MAX(limit_end, offset_last));
}
draw_key_link(i, scale, int(offset), int(offset_n), limit, limit_end);
draw_key(i, scale, int(offset), editor->is_key_selected(track, i), limit, limit_string);
continue;
}
draw_key(i, scale, int(offset), editor->is_key_selected(track, i), limit, limit_end);
@ -2541,7 +2577,8 @@ void AnimationTrackEdit::draw_key(int p_index, float p_pixels_sec, int p_x, bool
}
text += ")";
int limit = MAX(0, p_clip_right - p_x - icon_to_draw->get_width());
int limit = ((p_selected && editor->is_moving_selection()) || editor->is_function_name_pressed()) ? 0 : MAX(0, p_clip_right - p_x - icon_to_draw->get_width() * 2);
if (limit > 0) {
draw_string(font, Vector2(p_x + icon_to_draw->get_width(), int(get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)), text, HORIZONTAL_ALIGNMENT_LEFT, limit, font_size, color);
}
@ -2792,7 +2829,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
if (rect.has_point(p_pos)) {
if (const_cast<AnimationTrackEdit *>(this)->is_key_selectable_by_distance()) {
float distance = ABS(offset - p_pos.x);
float distance = Math::abs(offset - p_pos.x);
if (key_idx == -1 || distance < key_distance) {
key_idx = i;
key_distance = distance;
@ -3197,7 +3234,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (rect.has_point(pos)) {
if (is_key_selectable_by_distance()) {
const float distance = ABS(offset - pos.x);
const float distance = Math::abs(offset - pos.x);
if (key_idx == -1 || distance < key_distance) {
key_idx = i;
key_distance = distance;
@ -3261,7 +3298,7 @@ bool AnimationTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p_aggre
if (rect.has_point(p_pos)) {
if (is_key_selectable_by_distance()) {
float distance = ABS(offset - p_pos.x);
float distance = Math::abs(offset - p_pos.x);
if (key_idx == -1 || distance < key_distance) {
key_idx = i;
key_distance = distance;
@ -3324,7 +3361,7 @@ Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) {
Dictionary drag_data;
drag_data["type"] = "animation_track";
String base_path = animation->track_get_path(track);
base_path = base_path.get_slice(":", 0); // Remove sub-path.
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
drag_data["group"] = base_path;
drag_data["index"] = track;
@ -3355,7 +3392,7 @@ bool AnimationTrackEdit::can_drop_data(const Point2 &p_point, const Variant &p_d
// Don't allow moving tracks outside their groups.
if (get_editor()->is_grouping_tracks()) {
String base_path = animation->track_get_path(track);
base_path = base_path.get_slice(":", 0); // Remove sub-path.
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
if (d["group"] != base_path) {
return false;
}
@ -3386,7 +3423,7 @@ void AnimationTrackEdit::drop_data(const Point2 &p_point, const Variant &p_data)
// Don't allow moving tracks outside their groups.
if (get_editor()->is_grouping_tracks()) {
String base_path = animation->track_get_path(track);
base_path = base_path.get_slice(":", 0); // Remove sub-path.
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
if (d["group"] != base_path) {
return;
}
@ -3590,6 +3627,15 @@ void AnimationTrackEditGroup::_notification(int p_what) {
icon_size = Vector2(1, 1) * get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
//TODO
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Animation track group")));
} break;
case NOTIFICATION_DRAW: {
const Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
const int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
@ -3894,6 +3940,7 @@ bool AnimationTrackEditor::has_keying() const {
Dictionary AnimationTrackEditor::get_state() const {
Dictionary state;
state["fps_mode"] = timeline->is_using_fps();
state["fps_compat"] = fps_compat->is_pressed();
state["zoom"] = zoom->get_value();
state["offset"] = timeline->get_value();
state["v_scroll"] = scroll->get_v_scroll_bar()->get_value();
@ -3909,27 +3956,34 @@ void AnimationTrackEditor::set_state(const Dictionary &p_state) {
snap_mode->select(0);
}
_snap_mode_changed(snap_mode->get_selected());
} else {
snap_mode->select(0);
_snap_mode_changed(snap_mode->get_selected());
}
if (p_state.has("fps_compat")) {
fps_compat->set_pressed(p_state["fps_compat"]);
}
if (p_state.has("zoom")) {
zoom->set_value(p_state["zoom"]);
} else {
zoom->set_value(1.0);
}
if (p_state.has("offset")) {
timeline->set_value(p_state["offset"]);
} else {
timeline->set_value(0);
}
if (p_state.has("v_scroll")) {
scroll->get_v_scroll_bar()->set_value(p_state["v_scroll"]);
} else {
scroll->get_v_scroll_bar()->set_value(0);
}
}
void AnimationTrackEditor::clear() {
snap_mode->select(EDITOR_GET("editors/animation/default_fps_mode"));
_snap_mode_changed(snap_mode->get_selected());
fps_compat->set_pressed(EDITOR_GET("editors/animation/default_fps_compatibility"));
zoom->set_value(1.0);
timeline->set_value(0);
scroll->get_v_scroll_bar()->set_value(0);
}
void AnimationTrackEditor::cleanup() {
set_animation(Ref<Animation>(), read_only);
}
@ -4662,7 +4716,7 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
for (int i = 0; i < subindices.size(); i++) {
InsertData id = p_id;
id.type = Animation::TYPE_BEZIER;
id.value = subindices[i].is_empty() ? p_id.value : p_id.value.get(subindices[i].substr(1, subindices[i].length()));
id.value = subindices[i].is_empty() ? p_id.value : p_id.value.get(subindices[i].substr(1));
id.path = String(p_id.path) + subindices[i];
p_next_tracks = _confirm_insert(id, p_next_tracks, p_reset_wanted, p_reset_anim, false);
}
@ -4935,7 +4989,7 @@ void AnimationTrackEditor::_update_tracks() {
if (use_grouping) {
String base_path = animation->track_get_path(i);
base_path = base_path.get_slice(":", 0); // Remove sub-path.
base_path = base_path.get_slicec(':', 0); // Remove sub-path.
if (!group_sort.has(base_path)) {
AnimationTrackEditGroup *g = memnew(AnimationTrackEditGroup);
@ -5088,7 +5142,7 @@ void AnimationTrackEditor::_update_nearest_fps_label() {
nearest_fps_label->hide();
} else {
nearest_fps_label->show();
nearest_fps_label->set_text("Nearest FPS: " + itos(nearest_fps));
nearest_fps_label->set_text(vformat(TTR("Nearest FPS: %d"), nearest_fps));
}
}
@ -5154,6 +5208,7 @@ void AnimationTrackEditor::_notification(int p_what) {
snap_keys->set_button_icon(get_editor_theme_icon(SNAME("SnapKeys")));
fps_compat->set_button_icon(get_editor_theme_icon(SNAME("FPS")));
view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup")));
function_name_toggler->set_button_icon(get_editor_theme_icon(SNAME("MemberMethod")));
selected_filter->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter")));
imported_anim_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
dummy_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
@ -5168,6 +5223,8 @@ void AnimationTrackEditor::_notification(int p_what) {
const int track_separation = get_theme_constant(SNAME("track_v_separation"), SNAME("AnimationTrackEditor"));
track_vbox->add_theme_constant_override("separation", track_separation);
function_name_toggler->add_theme_color_override("icon_pressed_color", get_theme_color("icon_disabled_color", EditorStringName(Editor)));
} break;
case NOTIFICATION_READY: {
@ -5628,17 +5685,16 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) {
Dictionary d;
d["method"] = p_method;
Array params;
int first_defarg = E.arguments.size() - E.default_arguments.size();
int64_t first_defarg = E.arguments.size() - E.default_arguments.size();
int i = 0;
for (List<PropertyInfo>::ConstIterator itr = E.arguments.begin(); itr != E.arguments.end(); ++itr, ++i) {
for (int64_t i = 0; i < E.arguments.size(); ++i) {
if (i >= first_defarg) {
Variant arg = E.default_arguments[i - first_defarg];
params.push_back(arg);
} else {
Callable::CallError ce;
Variant arg;
Variant::construct(itr->type, arg, nullptr, 0, ce);
Variant::construct(E.arguments[i].type, arg, nullptr, 0, ce);
params.push_back(arg);
}
}
@ -6515,7 +6571,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
text = path;
int sep = text.find_char(':');
if (sep != -1) {
text = text.substr(sep + 1, text.length());
text = text.substr(sep + 1);
}
}
@ -6599,7 +6655,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
} break;
case EDIT_PASTE_TRACKS: {
if (track_clipboard.size() == 0) {
if (track_clipboard.is_empty()) {
EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!"));
break;
}
@ -6713,7 +6769,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
to_restore.push_back(amr);
}
#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * ABS(s) + from_t
#define NEW_POS(m_ofs) (((s > 0) ? m_ofs : from_t + (len - (m_ofs - from_t))) - pivot) * Math::abs(s) + from_t
// 3 - Move the keys (re insert them).
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float newpos = NEW_POS(E->get().pos);
@ -6874,8 +6930,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
if (is_using_angle) {
real_t a = from_v;
real_t b = to_v;
real_t to_diff = fmod(b - a, Math_TAU);
to_v = a + fmod(2.0 * to_diff, Math_TAU) - to_diff;
real_t to_diff = fmod(b - a, Math::TAU);
to_v = a + fmod(2.0 * to_diff, Math::TAU) - to_diff;
}
Variant delta_v = Animation::subtract_variant(to_v, from_v);
double duration = to_t - from_t;
@ -7338,6 +7394,10 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
_update_tracks();
}
void AnimationTrackEditor::_toggle_function_names() {
_redraw_tracks();
}
void AnimationTrackEditor::_view_group_toggle() {
_update_tracks();
view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup")));
@ -7352,6 +7412,10 @@ bool AnimationTrackEditor::is_grouping_tracks() {
return !view_group->is_pressed();
}
bool AnimationTrackEditor::is_function_name_pressed() {
return function_name_toggler->is_pressed();
}
void AnimationTrackEditor::_auto_fit() {
timeline->auto_fit();
}
@ -7642,14 +7706,26 @@ AnimationTrackEditor::AnimationTrackEditor() {
bezier_edit_icon->set_toggle_mode(true);
bezier_edit_icon->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_toggle_bezier_edit));
bezier_edit_icon->set_tooltip_text(TTR("Toggle between the bezier curve editor and track editor."));
bezier_edit_icon->set_accessibility_name(TTRC("Bezier Curve Editor"));
bottom_hf->add_child(bezier_edit_icon);
function_name_toggler = memnew(Button);
function_name_toggler->set_flat(true);
function_name_toggler->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_toggle_function_names));
function_name_toggler->set_shortcut(ED_SHORTCUT("animation_editor/toggle_function_names", TTRC("Toggle method names")));
function_name_toggler->set_toggle_mode(true);
function_name_toggler->set_shortcut_in_tooltip(false);
function_name_toggler->set_tooltip_text(TTRC("Toggle function names in the track editor."));
bottom_hf->add_child(function_name_toggler);
selected_filter = memnew(Button);
selected_filter->set_flat(true);
selected_filter->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); // Same function works the same.
selected_filter->set_toggle_mode(true);
selected_filter->set_tooltip_text(TTR("Only show tracks from nodes selected in tree."));
selected_filter->set_accessibility_name(TTRC("Show Tracks from Selected Nodes"));
bottom_hf->add_child(selected_filter);
@ -7658,6 +7734,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
view_group->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_view_group_toggle));
view_group->set_toggle_mode(true);
view_group->set_tooltip_text(TTR("Group tracks by node or display them as plain list."));
view_group->set_accessibility_name(TTRC("Group Tracks by Node"));
bottom_hf->add_child(view_group);
bottom_hf->add_child(memnew(VSeparator));
@ -7669,6 +7746,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
snap_timeline->set_toggle_mode(true);
snap_timeline->set_pressed(false);
snap_timeline->set_tooltip_text(TTR("Apply snapping to timeline cursor."));
snap_timeline->set_accessibility_name(TTRC("Apply Snapping to Cursor"));
snap_keys = memnew(Button);
snap_keys->set_flat(true);
@ -7677,6 +7755,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
snap_keys->set_toggle_mode(true);
snap_keys->set_pressed(true);
snap_keys->set_tooltip_text(TTR("Apply snapping to selected key(s)."));
snap_keys->set_accessibility_name(TTRC("Apply Snapping to Selected Key"));
fps_compat = memnew(Button);
fps_compat->set_flat(true);
@ -7685,9 +7764,11 @@ AnimationTrackEditor::AnimationTrackEditor() {
fps_compat->set_toggle_mode(true);
fps_compat->set_pressed(true);
fps_compat->set_tooltip_text(TTR("Apply snapping to the nearest integer FPS."));
fps_compat->set_accessibility_name(TTRC("Apply Snapping to Nearest Integer FPS"));
fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode));
nearest_fps_label = memnew(Label);
nearest_fps_label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
bottom_hf->add_child(nearest_fps_label);
step = memnew(EditorSpinSlider);
@ -7697,6 +7778,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
step->set_hide_slider(true);
step->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
step->set_tooltip_text(TTR("Animation step value."));
step->set_accessibility_name(TTRC("Animation Step Value"));
bottom_hf->add_child(step);
step->connect(SceneStringName(value_changed), callable_mp(this, &AnimationTrackEditor::_update_step));
step->set_read_only(true);
@ -7704,9 +7786,10 @@ AnimationTrackEditor::AnimationTrackEditor() {
snap_mode = memnew(OptionButton);
snap_mode->add_item(TTR("Seconds"));
snap_mode->add_item(TTR("FPS"));
snap_mode->set_accessibility_name(TTRC("Snap Mode"));
snap_mode->set_disabled(true);
bottom_hf->add_child(snap_mode);
snap_mode->connect(SceneStringName(item_selected), callable_mp(this, &AnimationTrackEditor::_snap_mode_changed));
snap_mode->set_disabled(true);
bottom_hf->add_child(memnew(VSeparator));
@ -7721,6 +7804,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
zoom->set_value(1.0);
zoom->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
zoom->set_v_size_flags(SIZE_SHRINK_CENTER);
zoom->set_accessibility_name(TTRC("Zoom"));
zoom_hb->add_child(zoom);
bottom_hf->add_child(zoom_hb);
timeline->set_zoom(zoom);
@ -7731,6 +7815,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
auto_fit->set_flat(true);
auto_fit->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit));
auto_fit->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit"));
auto_fit->set_accessibility_name(TTRC("Auto Fit"));
bottom_hf->add_child(auto_fit);
auto_fit_bezier = memnew(Button);
@ -7738,6 +7823,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
auto_fit_bezier->set_visible(false);
auto_fit_bezier->connect(SceneStringName(pressed), callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier));
auto_fit_bezier->set_shortcut(ED_GET_SHORTCUT("animation_editor/auto_fit"));
auto_fit_bezier->set_accessibility_name(TTRC("Auto Fit Bezier"));
bottom_hf->add_child(auto_fit_bezier);
edit = memnew(MenuButton);
@ -7746,6 +7832,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
edit->set_flat(false);
edit->set_disabled(true);
edit->set_tooltip_text(TTR("Animation properties."));
edit->set_accessibility_name(TTRC("Animation Properties"));
edit->get_popup()->add_item(TTR("Copy Tracks..."), EDIT_COPY_TRACKS);
edit->get_popup()->add_item(TTR("Paste Tracks"), EDIT_PASTE_TRACKS);
edit->get_popup()->add_separator();
@ -7790,10 +7877,12 @@ AnimationTrackEditor::AnimationTrackEditor() {
prop_selector = memnew(PropertySelector);
add_child(prop_selector);
prop_selector->connect("selected", callable_mp(this, &AnimationTrackEditor::_new_track_property_selected));
prop_selector->set_accessibility_name(TTRC("Track Property"));
method_selector = memnew(PropertySelector);
add_child(method_selector);
method_selector->connect("selected", callable_mp(this, &AnimationTrackEditor::_add_method_key));
method_selector->set_accessibility_name(TTRC("Method Key"));
insert_confirm = memnew(ConfirmationDialog);
add_child(insert_confirm);
@ -7838,18 +7927,21 @@ AnimationTrackEditor::AnimationTrackEditor() {
optimize_velocity_error->set_min(0.001);
optimize_velocity_error->set_step(0.001);
optimize_velocity_error->set_value(0.01);
optimize_velocity_error->set_accessibility_name(TTRC("Max Velocity Error"));
optimize_vb->add_margin_child(TTR("Max Velocity Error:"), optimize_velocity_error);
optimize_angular_error = memnew(SpinBox);
optimize_angular_error->set_max(1.0);
optimize_angular_error->set_min(0.001);
optimize_angular_error->set_step(0.001);
optimize_angular_error->set_value(0.01);
optimize_angular_error->set_accessibility_name(TTRC("Max Angular Error"));
optimize_vb->add_margin_child(TTR("Max Angular Error:"), optimize_angular_error);
optimize_precision_error = memnew(SpinBox);
optimize_precision_error->set_max(6);
optimize_precision_error->set_min(1);
optimize_precision_error->set_step(1);
optimize_precision_error->set_value(3);
optimize_precision_error->set_accessibility_name(TTRC("Max Precision Error"));
optimize_vb->add_margin_child(TTR("Max Precision Error:"), optimize_precision_error);
optimize_dialog->set_ok_button_text(TTR("Optimize"));
@ -7900,6 +7992,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
scale->set_max(99999);
scale->set_step(0.001);
scale->set_select_all_on_focus(true);
scale->set_accessibility_name(TTRC("Scale Ratio"));
vbc->add_margin_child(TTR("Scale Ratio:"), scale);
scale_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM), CONNECT_DEFERRED);
add_child(scale_dialog);
@ -7915,6 +8008,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
ease_grid->set_columns(2);
ease_dialog->add_child(ease_grid);
transition_selection = memnew(OptionButton);
transition_selection->set_accessibility_name(TTRC("Transition Type"));
transition_selection->add_item(TTR("Linear", "Transition Type"), Tween::TRANS_LINEAR);
transition_selection->add_item(TTR("Sine", "Transition Type"), Tween::TRANS_SINE);
transition_selection->add_item(TTR("Quint", "Transition Type"), Tween::TRANS_QUINT);
@ -7930,6 +8024,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
transition_selection->select(Tween::TRANS_LINEAR); // Default
transition_selection->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Translation context is needed.
ease_selection = memnew(OptionButton);
ease_selection->set_accessibility_name(TTRC("Ease Type"));
ease_selection->add_item(TTR("In", "Ease Type"), Tween::EASE_IN);
ease_selection->add_item(TTR("Out", "Ease Type"), Tween::EASE_OUT);
ease_selection->add_item(TTR("InOut", "Ease Type"), Tween::EASE_IN_OUT);
@ -7941,6 +8036,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
ease_fps->set_max(999);
ease_fps->set_step(FPS_DECIMAL);
ease_fps->set_value(30); // Default
ease_fps->set_accessibility_name(TTRC("FPS"));
ease_grid->add_child(memnew(Label(TTR("Transition Type:"))));
ease_grid->add_child(transition_selection);
ease_grid->add_child(memnew(Label(TTR("Ease Type:"))));
@ -7957,12 +8053,16 @@ AnimationTrackEditor::AnimationTrackEditor() {
bake_grid->set_columns(2);
bake_dialog->add_child(bake_grid);
bake_trs = memnew(CheckBox);
bake_trs->set_accessibility_name(TTRC("3D Pos/Rot/Scl Track"));
bake_trs->set_pressed(true);
bake_blendshape = memnew(CheckBox);
bake_blendshape->set_accessibility_name(TTRC("Blendshape Track"));
bake_blendshape->set_pressed(true);
bake_value = memnew(CheckBox);
bake_value->set_accessibility_name(TTRC("Value Track"));
bake_value->set_pressed(true);
bake_fps = memnew(SpinBox);
bake_fps->set_accessibility_name(TTRC("FPS"));
bake_fps->set_min(FPS_DECIMAL);
bake_fps->set_max(999);
bake_fps->set_step(FPS_DECIMAL);
@ -7990,6 +8090,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
track_copy_vbox->add_child(select_all_button);
track_copy_select = memnew(Tree);
track_copy_select->set_accessibility_name(TTRC("Copy Selection"));
track_copy_select->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
track_copy_select->set_h_size_flags(SIZE_EXPAND_FILL);
track_copy_select->set_v_size_flags(SIZE_EXPAND_FILL);
@ -8110,9 +8211,6 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
spinner->connect("value_focus_exited", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
}
AnimationTrackKeyEditEditor::~AnimationTrackKeyEditEditor() {
}
void AnimationMarkerEdit::_zoom_changed() {
queue_redraw();
play_position->queue_redraw();
@ -8329,6 +8427,15 @@ void AnimationMarkerEdit::_notification(int p_what) {
selected_icon = get_editor_theme_icon(SNAME("MarkerSelected"));
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
//TODO
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Animation marker editor")));
} break;
case NOTIFICATION_DRAW: {
if (animation.is_null()) {
return;
@ -8635,7 +8742,7 @@ String AnimationMarkerEdit::get_tooltip(const Point2 &p_pos) const {
if (rect.has_point(p_pos)) {
if (const_cast<AnimationMarkerEdit *>(this)->is_key_selectable_by_distance()) {
float distance = ABS(offset - p_pos.x);
float distance = Math::abs(offset - p_pos.x);
if (key_idx == -1 || distance < key_distance) {
key_idx = i;
key_distance = distance;
@ -8684,8 +8791,8 @@ PackedStringArray AnimationMarkerEdit::get_selected_section() const {
PackedStringArray arr;
arr.push_back(""); // Marker with smallest time.
arr.push_back(""); // Marker with largest time.
double min_time = INFINITY;
double max_time = -INFINITY;
double min_time = Math::INF;
double max_time = -Math::INF;
for (const StringName &marker_name : selection) {
double time = animation->get_marker_time(marker_name);
if (time < min_time) {
@ -9101,9 +9208,6 @@ AnimationMarkerEdit::AnimationMarkerEdit() {
marker_rename_confirm->add_child(marker_rename_error_dialog);
}
AnimationMarkerEdit::~AnimationMarkerEdit() {
}
float AnimationMarkerKeyEdit::get_time() const {
return animation->get_marker_time(marker_name);
}
@ -9291,6 +9395,3 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim
spinner->connect("ungrabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
spinner->connect("value_focus_exited", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
}
AnimationMarkerKeyEditEditor::~AnimationMarkerKeyEditEditor() {
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ANIMATION_TRACK_EDITOR_H
#define ANIMATION_TRACK_EDITOR_H
#pragma once
#include "editor/editor_data.h"
#include "editor/editor_properties.h"
@ -404,7 +403,6 @@ public:
void _clear_selection(bool p_update);
AnimationMarkerEdit();
~AnimationMarkerEdit();
};
class AnimationTrackEdit : public Control {
@ -803,6 +801,9 @@ class AnimationTrackEditor : public VBoxContainer {
void _anim_paste_keys(float p_ofs, bool p_ofs_valid, int p_track);
void _toggle_function_names();
Button *function_name_toggler = nullptr;
void _view_group_toggle();
Button *view_group = nullptr;
Button *selected_filter = nullptr;
@ -916,6 +917,7 @@ public:
Dictionary get_state() const;
void set_state(const Dictionary &p_state);
void clear();
void cleanup();
@ -947,6 +949,7 @@ public:
bool is_marker_selected(const StringName &p_marker) const;
bool is_marker_moving_selection() const;
float get_marker_moving_selection_offset() const;
bool is_function_name_pressed();
/** If `p_from_mouse_event` is `true`, handle Shift key presses for precise snapping. */
void goto_prev_step(bool p_from_mouse_event);
@ -983,7 +986,6 @@ class AnimationTrackKeyEditEditor : public EditorProperty {
public:
AnimationTrackKeyEditEditor(Ref<Animation> p_animation, int p_track, real_t p_key_ofs, bool p_use_fps);
~AnimationTrackKeyEditEditor();
};
// AnimationMarkerKeyEditEditorPlugin
@ -1001,7 +1003,4 @@ class AnimationMarkerKeyEditEditor : public EditorProperty {
public:
AnimationMarkerKeyEditEditor(Ref<Animation> p_animation, const StringName &p_name, bool p_use_fps);
~AnimationMarkerKeyEditEditor();
};
#endif // ANIMATION_TRACK_EDITOR_H

View file

@ -1051,13 +1051,13 @@ void AnimationTrackEditTypeAudio::gui_input(const Ref<InputEvent> &p_event) {
int end = ofs + len * get_timeline()->get_zoom_scale();
if (end >= get_timeline()->get_name_limit() && end <= get_size().width - get_timeline()->get_buttons_width() && ABS(mm->get_position().x - end) < 5 * EDSCALE) {
if (end >= get_timeline()->get_name_limit() && end <= get_size().width - get_timeline()->get_buttons_width() && Math::abs(mm->get_position().x - end) < 5 * EDSCALE) {
len_resizing_start = false;
use_hsize_cursor = true;
len_resizing_index = i;
}
if (ofs >= get_timeline()->get_name_limit() && ofs <= get_size().width - get_timeline()->get_buttons_width() && ABS(mm->get_position().x - ofs) < 5 * EDSCALE) {
if (ofs >= get_timeline()->get_name_limit() && ofs <= get_size().width - get_timeline()->get_buttons_width() && Math::abs(mm->get_position().x - ofs) < 5 * EDSCALE) {
len_resizing_start = true;
use_hsize_cursor = true;
len_resizing_index = i;
@ -1322,9 +1322,6 @@ void AnimationTrackEditTypeAnimation::set_node(Object *p_object) {
id = p_object->get_instance_id();
}
AnimationTrackEditTypeAnimation::AnimationTrackEditTypeAnimation() {
}
/////////
AnimationTrackEdit *AnimationTrackEditDefaultPlugin::create_value_track_edit(Object *p_object, Variant::Type p_type, const String &p_property, PropertyHint p_hint, const String &p_hint_string, int p_usage) {
if (p_property == "playing" && (p_object->is_class("AudioStreamPlayer") || p_object->is_class("AudioStreamPlayer2D") || p_object->is_class("AudioStreamPlayer3D"))) {

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef ANIMATION_TRACK_EDITOR_PLUGINS_H
#define ANIMATION_TRACK_EDITOR_PLUGINS_H
#pragma once
#include "editor/animation_track_editor.h"
@ -88,8 +87,6 @@ public:
void set_node(Object *p_object);
void set_as_coords();
AnimationTrackEditSpriteFrame() {}
};
class AnimationTrackEditSubAnim : public AnimationTrackEdit {
@ -146,7 +143,6 @@ public:
virtual void draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) override;
void set_node(Object *p_object);
AnimationTrackEditTypeAnimation();
};
class AnimationTrackEditVolumeDB : public AnimationTrackEdit {
@ -167,5 +163,3 @@ public:
virtual AnimationTrackEdit *create_audio_track_edit() override;
virtual AnimationTrackEdit *create_animation_track_edit(Object *p_object) override;
};
#endif // ANIMATION_TRACK_EDITOR_PLUGINS_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef AUDIO_STREAM_PREVIEW_H
#define AUDIO_STREAM_PREVIEW_H
#pragma once
#include "core/os/thread.h"
#include "core/templates/safe_refcount.h"
@ -104,5 +103,3 @@ public:
AudioStreamPreviewGenerator();
};
#endif // AUDIO_STREAM_PREVIEW_H

View file

@ -123,6 +123,7 @@ GotoLinePopup::GotoLinePopup() {
line_input->set_select_all_on_focus(true);
line_input->connect(SceneStringName(text_changed), callable_mp(this, &GotoLinePopup::_goto_line).unbind(1));
line_input->connect(SceneStringName(text_submitted), callable_mp(this, &GotoLinePopup::_submit).unbind(1));
line_input->set_accessibility_name(TTRC("Line Number"));
vbc->add_child(line_input);
}
@ -144,6 +145,11 @@ void FindReplaceBar::_notification(int p_what) {
_update_toggle_replace_button(replace_text->is_visible_in_tree());
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
_update_toggle_replace_button(replace_text->is_visible_in_tree());
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
set_process_input(is_visible_in_tree());
} break;
@ -748,6 +754,7 @@ void FindReplaceBar::_bind_methods() {
FindReplaceBar::FindReplaceBar() {
toggle_replace_button = memnew(Button);
add_child(toggle_replace_button);
toggle_replace_button->set_accessibility_name(TTRC("Replace Mode"));
toggle_replace_button->set_flat(true);
toggle_replace_button->set_focus_mode(FOCUS_NONE);
toggle_replace_button->connect(SceneStringName(pressed), callable_mp(this, &FindReplaceBar::_toggle_replace_pressed));
@ -779,6 +786,7 @@ FindReplaceBar::FindReplaceBar() {
vbc_lineedit->add_child(search_text);
search_text->set_placeholder(TTR("Find"));
search_text->set_tooltip_text(TTR("Find"));
search_text->set_accessibility_name(TTRC("Find"));
search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
search_text->connect(SceneStringName(text_changed), callable_mp(this, &FindReplaceBar::_search_text_changed));
search_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindReplaceBar::_search_text_submitted));
@ -790,6 +798,7 @@ FindReplaceBar::FindReplaceBar() {
find_prev = memnew(Button);
find_prev->set_flat(true);
find_prev->set_tooltip_text(TTR("Previous Match"));
find_prev->set_accessibility_name(TTRC("Previous Match"));
hbc_button_search->add_child(find_prev);
find_prev->set_focus_mode(FOCUS_NONE);
find_prev->connect(SceneStringName(pressed), callable_mp(this, &FindReplaceBar::search_prev));
@ -797,6 +806,7 @@ FindReplaceBar::FindReplaceBar() {
find_next = memnew(Button);
find_next->set_flat(true);
find_next->set_tooltip_text(TTR("Next Match"));
find_next->set_accessibility_name(TTRC("Next Match"));
hbc_button_search->add_child(find_next);
find_next->set_focus_mode(FOCUS_NONE);
find_next->connect(SceneStringName(pressed), callable_mp(this, &FindReplaceBar::search_next));
@ -818,6 +828,7 @@ FindReplaceBar::FindReplaceBar() {
vbc_lineedit->add_child(replace_text);
replace_text->set_placeholder(TTR("Replace"));
replace_text->set_tooltip_text(TTR("Replace"));
replace_text->set_accessibility_name(TTRC("Replace"));
replace_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
replace_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindReplaceBar::_replace_text_submitted));
@ -840,6 +851,7 @@ FindReplaceBar::FindReplaceBar() {
hide_button = memnew(TextureButton);
add_child(hide_button);
hide_button->set_tooltip_text(TTR("Hide"));
hide_button->set_accessibility_name(TTRC("Hide"));
hide_button->set_focus_mode(FOCUS_NONE);
hide_button->connect(SceneStringName(pressed), callable_mp(this, &FindReplaceBar::_hide_bar));
hide_button->set_v_size_flags(SIZE_SHRINK_CENTER);
@ -1622,10 +1634,10 @@ void CodeTextEditor::_set_show_warnings_panel(bool p_show) {
emit_signal(SNAME("show_warnings_panel"), p_show);
}
void CodeTextEditor::_toggle_scripts_pressed() {
ERR_FAIL_NULL(toggle_scripts_list);
toggle_scripts_list->set_visible(!toggle_scripts_list->is_visible());
update_toggle_scripts_button();
void CodeTextEditor::_toggle_files_pressed() {
ERR_FAIL_NULL(toggle_files_list);
toggle_files_list->set_visible(!toggle_files_list->is_visible());
update_toggle_files_button();
}
void CodeTextEditor::_error_pressed(const Ref<InputEvent> &p_event) {
@ -1643,15 +1655,22 @@ void CodeTextEditor::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
if (toggle_scripts_button->is_visible()) {
update_toggle_scripts_button();
if (toggle_files_button->is_visible()) {
update_toggle_files_button();
}
_update_text_editor_theme();
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
if (toggle_files_button->is_visible()) {
update_toggle_files_button();
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (toggle_scripts_button->is_visible()) {
update_toggle_scripts_button();
if (toggle_files_button->is_visible()) {
update_toggle_files_button();
}
set_process_input(is_visible_in_tree());
} break;
@ -1718,7 +1737,7 @@ void CodeTextEditor::toggle_bookmark() {
void CodeTextEditor::goto_next_bookmark() {
PackedInt32Array bmarks = text_editor->get_bookmarked_lines();
if (bmarks.size() <= 0) {
if (bmarks.is_empty()) {
return;
}
@ -1734,7 +1753,7 @@ void CodeTextEditor::goto_next_bookmark() {
void CodeTextEditor::goto_prev_bookmark() {
PackedInt32Array bmarks = text_editor->get_bookmarked_lines();
if (bmarks.size() <= 0) {
if (bmarks.is_empty()) {
return;
}
@ -1808,18 +1827,18 @@ void CodeTextEditor::set_code_complete_func(CodeTextEditorCodeCompleteFunc p_cod
}
void CodeTextEditor::set_toggle_list_control(Control *p_control) {
toggle_scripts_list = p_control;
toggle_files_list = p_control;
}
void CodeTextEditor::show_toggle_scripts_button() {
toggle_scripts_button->show();
void CodeTextEditor::show_toggle_files_button() {
toggle_files_button->show();
}
void CodeTextEditor::update_toggle_scripts_button() {
ERR_FAIL_NULL(toggle_scripts_list);
bool forward = toggle_scripts_list->is_visible() == is_layout_rtl();
toggle_scripts_button->set_button_icon(get_editor_theme_icon(forward ? SNAME("Forward") : SNAME("Back")));
toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text()));
void CodeTextEditor::update_toggle_files_button() {
ERR_FAIL_NULL(toggle_files_list);
bool forward = toggle_files_list->is_visible() == is_layout_rtl();
toggle_files_button->set_button_icon(get_editor_theme_icon(forward ? SNAME("Forward") : SNAME("Back")));
toggle_files_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Files Panel"), ED_GET_SHORTCUT("script_editor/toggle_files_panel")->get_as_text()));
}
CodeTextEditor::CodeTextEditor() {
@ -1856,12 +1875,13 @@ CodeTextEditor::CodeTextEditor() {
error_line = 0;
error_column = 0;
toggle_scripts_button = memnew(Button);
toggle_scripts_button->set_flat(true);
toggle_scripts_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
toggle_scripts_button->connect(SceneStringName(pressed), callable_mp(this, &CodeTextEditor::_toggle_scripts_pressed));
status_bar->add_child(toggle_scripts_button);
toggle_scripts_button->hide();
toggle_files_button = memnew(Button);
toggle_files_button->set_flat(true);
toggle_files_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
toggle_files_button->connect(SceneStringName(pressed), callable_mp(this, &CodeTextEditor::_toggle_files_pressed));
toggle_files_button->set_accessibility_name(TTRC("Scripts"));
status_bar->add_child(toggle_files_button);
toggle_files_button->hide();
// Error
ScrollContainer *scroll = memnew(ScrollContainer);
@ -1884,6 +1904,7 @@ CodeTextEditor::CodeTextEditor() {
error_button->set_default_cursor_shape(CURSOR_POINTING_HAND);
error_button->connect(SceneStringName(pressed), callable_mp(this, &CodeTextEditor::_error_button_pressed));
error_button->set_tooltip_text(TTR("Errors"));
error_button->set_accessibility_name(TTRC("Errors"));
// Warnings
warning_button = memnew(Button);
@ -1893,6 +1914,7 @@ CodeTextEditor::CodeTextEditor() {
warning_button->set_default_cursor_shape(CURSOR_POINTING_HAND);
warning_button->connect(SceneStringName(pressed), callable_mp(this, &CodeTextEditor::_warning_button_pressed));
warning_button->set_tooltip_text(TTR("Warnings"));
warning_button->set_accessibility_name(TTRC("Warnings"));
status_bar->add_child(memnew(VSeparator));
@ -1906,9 +1928,10 @@ CodeTextEditor::CodeTextEditor() {
// TRANSLATORS: The placeholders are keyboard shortcuts. The first one is in the form of "Ctrl+"/"Cmd+".
vformat(TTR("%sMouse wheel, %s/%s: Finetune\n%s: Reset"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL), ED_GET_SHORTCUT("script_editor/zoom_in")->get_as_text(), ED_GET_SHORTCUT("script_editor/zoom_out")->get_as_text(), ED_GET_SHORTCUT("script_editor/reset_zoom")->get_as_text()));
zoom_button->set_text("100 %");
zoom_button->set_accessibility_name(TTRC("Zoom Factor"));
PopupMenu *zoom_menu = zoom_button->get_popup();
int preset_count = sizeof(ZOOM_FACTOR_PRESETS) / sizeof(float);
constexpr int preset_count = std::size(ZOOM_FACTOR_PRESETS);
for (int i = 0; i < preset_count; i++) {
float z = ZOOM_FACTOR_PRESETS[i];
@ -1925,6 +1948,7 @@ CodeTextEditor::CodeTextEditor() {
status_bar->add_child(line_and_col_txt);
line_and_col_txt->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
line_and_col_txt->set_tooltip_text(TTR("Line and column numbers."));
line_and_col_txt->set_accessibility_name(TTRC("Line and Column Numbers"));
line_and_col_txt->set_mouse_filter(MOUSE_FILTER_STOP);
status_bar->add_child(memnew(VSeparator));
@ -1934,21 +1958,14 @@ CodeTextEditor::CodeTextEditor() {
status_bar->add_child(indentation_txt);
indentation_txt->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
indentation_txt->set_tooltip_text(TTR("Indentation"));
indentation_txt->set_accessibility_name(TTRC("Indentation"));
indentation_txt->set_mouse_filter(MOUSE_FILTER_STOP);
text_editor->connect(SceneStringName(gui_input), callable_mp(this, &CodeTextEditor::_text_editor_gui_input));
text_editor->connect("caret_changed", callable_mp(this, &CodeTextEditor::_line_col_changed));
text_editor->connect(SceneStringName(text_changed), callable_mp(this, &CodeTextEditor::_text_changed));
text_editor->connect("code_completion_requested", callable_mp(this, &CodeTextEditor::_complete_request));
TypedArray<String> cs;
cs.push_back(".");
cs.push_back(",");
cs.push_back("(");
cs.push_back("=");
cs.push_back("$");
cs.push_back("@");
cs.push_back("\"");
cs.push_back("\'");
TypedArray<String> cs = { ".", ",", "(", "=", "$", "@", "\"", "\'" };
text_editor->set_code_completion_prefixes(cs);
idle->connect("timeout", callable_mp(this, &CodeTextEditor::_text_changed_idle_timeout));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CODE_EDITOR_H
#define CODE_EDITOR_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -166,8 +165,8 @@ class CodeTextEditor : public VBoxContainer {
FindReplaceBar *find_replace_bar = nullptr;
HBoxContainer *status_bar = nullptr;
Button *toggle_scripts_button = nullptr;
Control *toggle_scripts_list = nullptr;
Button *toggle_files_button = nullptr;
Control *toggle_files_list = nullptr;
Button *error_button = nullptr;
Button *warning_button = nullptr;
@ -221,7 +220,7 @@ class CodeTextEditor : public VBoxContainer {
void _zoom_popup_id_pressed(int p_idx);
void _toggle_scripts_pressed();
void _toggle_files_pressed();
protected:
virtual void _load_theme_settings() {}
@ -299,10 +298,8 @@ public:
void validate_script();
void set_toggle_list_control(Control *p_control);
void show_toggle_scripts_button();
void update_toggle_scripts_button();
void show_toggle_files_button();
void update_toggle_files_button();
CodeTextEditor();
};
#endif // CODE_EDITOR_H

View file

@ -81,7 +81,7 @@ public:
String name = p_name;
if (name.begins_with("bind/argument_")) {
int which = name.get_slice("_", 1).to_int() - 1;
int which = name.get_slicec('_', 1).to_int() - 1;
ERR_FAIL_INDEX_V(which, params.size(), false);
params.write[which] = p_value;
} else {
@ -95,7 +95,7 @@ public:
String name = p_name;
if (name.begins_with("bind/argument_")) {
int which = name.get_slice("_", 1).to_int() - 1;
int which = name.get_slicec('_', 1).to_int() - 1;
ERR_FAIL_INDEX_V(which, params.size(), false);
r_ret = params[which];
} else {
@ -224,7 +224,7 @@ void ConnectDialog::_remove_bind() {
if (st.is_empty()) {
return;
}
int idx = st.get_slice("/", 1).to_int() - 1;
int idx = st.get_slicec('/', 1).to_int() - 1;
ERR_FAIL_INDEX(idx, cdbinds->params.size());
cdbinds->params.remove_at(idx);
@ -255,10 +255,12 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, const St
subst["NodeName"] = node_name.to_pascal_case();
subst["nodeName"] = node_name.to_camel_case();
subst["node_name"] = node_name.to_snake_case();
subst["node-name"] = node_name.to_kebab_case();
subst["SignalName"] = p_signal_name.to_pascal_case();
subst["signalName"] = p_signal_name.to_camel_case();
subst["signal_name"] = p_signal_name.to_snake_case();
subst["signal-name"] = p_signal_name.to_kebab_case();
String dst_method;
if (p_source == p_target) {
@ -282,10 +284,11 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
bool check_signal = compatible_methods_only->is_pressed();
List<MethodInfo> ret;
List<Pair<Variant::Type, StringName>> effective_args;
LocalVector<Pair<Variant::Type, StringName>> effective_args;
int unbind = get_unbinds();
for (int i = 0; i < p_signal.arguments.size() - unbind; i++) {
PropertyInfo pi = p_signal.arguments.get(i);
effective_args.reserve(p_signal.arguments.size() - unbind);
for (int64_t i = 0; i < p_signal.arguments.size() - unbind; i++) {
PropertyInfo pi = p_signal.arguments[i];
effective_args.push_back(Pair(pi.type, pi.class_name));
}
if (unbind == 0) {
@ -312,17 +315,16 @@ List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_me
}
bool type_mismatch = false;
const List<Pair<Variant::Type, StringName>>::Element *E = effective_args.front();
for (const List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next(), E = E->next()) {
Variant::Type stype = E->get().first;
Variant::Type mtype = F->get().type;
for (int64_t i = 0; i < mi.arguments.size(); ++i) {
Variant::Type stype = effective_args[i].first;
Variant::Type mtype = mi.arguments[i].type;
if (stype != Variant::NIL && mtype != Variant::NIL && stype != mtype) {
type_mismatch = true;
break;
}
if (stype == Variant::OBJECT && mtype == Variant::OBJECT && !ClassDB::is_parent_class(E->get().second, F->get().class_name)) {
if (stype == Variant::OBJECT && mtype == Variant::OBJECT && !ClassDB::is_parent_class(effective_args[i].second, mi.arguments[i].class_name)) {
type_mismatch = true;
break;
}
@ -453,7 +455,13 @@ void ConnectDialog::_update_ok_enabled() {
}
void ConnectDialog::_update_warning_label() {
Ref<Script> scr = source->get_node(dst_path)->get_script();
Node *dst = source->get_node(dst_path);
if (dst == nullptr) {
warning_label->set_visible(false);
return;
}
Ref<Script> scr = dst->get_script();
if (scr.is_null()) {
warning_label->set_visible(false);
return;
@ -546,13 +554,12 @@ String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArra
signature.append(p_method.name);
signature.append("(");
int i = 0;
for (List<PropertyInfo>::ConstIterator itr = p_method.arguments.begin(); itr != p_method.arguments.end(); ++itr, ++i) {
if (itr != p_method.arguments.begin()) {
for (int64_t i = 0; i < p_method.arguments.size(); ++i) {
if (i > 0) {
signature.append(", ");
}
const PropertyInfo &pi = *itr;
const PropertyInfo &pi = p_method.arguments[i];
String type_name;
switch (pi.type) {
case Variant::NIL:
@ -575,8 +582,8 @@ String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArra
case Variant::DICTIONARY:
type_name = "Dictionary";
if (pi.hint == PROPERTY_HINT_DICTIONARY_TYPE && !pi.hint_string.is_empty()) {
String key_hint = pi.hint_string.get_slice(";", 0);
String value_hint = pi.hint_string.get_slice(";", 1);
String key_hint = pi.hint_string.get_slicec(';', 0);
String value_hint = pi.hint_string.get_slicec(';', 1);
if (key_hint.is_empty() || key_hint.begins_with("res://")) {
key_hint = "Variant";
}
@ -731,6 +738,7 @@ ConnectDialog::ConnectDialog() {
vbc_left->set_custom_minimum_size(Vector2(400 * EDSCALE, 0));
from_signal = memnew(LineEdit);
from_signal->set_accessibility_name(TTRC("From Signal"));
vbc_left->add_margin_child(TTR("From Signal:"), from_signal);
from_signal->set_editable(false);
@ -749,6 +757,7 @@ ConnectDialog::ConnectDialog() {
hbc_filter->add_child(filter_nodes);
filter_nodes->set_h_size_flags(Control::SIZE_FILL | Control::SIZE_EXPAND);
filter_nodes->set_placeholder(TTR("Filter Nodes"));
filter_nodes->set_accessibility_name(TTRC("Filter Nodes"));
filter_nodes->set_clear_button_enabled(true);
filter_nodes->connect(SceneStringName(text_changed), callable_mp(tree, &SceneTreeEditor::set_filter));
@ -781,11 +790,13 @@ ConnectDialog::ConnectDialog() {
method_search = memnew(LineEdit);
method_vbc->add_child(method_search);
method_search->set_placeholder(TTR("Filter Methods"));
method_search->set_accessibility_name(TTRC("Filter Methods"));
method_search->set_clear_button_enabled(true);
method_search->connect(SceneStringName(text_changed), callable_mp(this, &ConnectDialog::_update_method_tree).unbind(1));
method_tree = memnew(Tree);
method_vbc->add_child(method_tree);
method_tree->set_accessibility_name(TTRC("Methods"));
method_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
method_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
method_tree->set_hide_root(true);
@ -819,6 +830,7 @@ ConnectDialog::ConnectDialog() {
HBoxContainer *add_bind_hb = memnew(HBoxContainer);
type_list = memnew(OptionButton);
type_list->set_accessibility_name(TTRC("Type"));
type_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
add_bind_hb->add_child(type_list);
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@ -846,12 +858,14 @@ ConnectDialog::ConnectDialog() {
vbc_right->add_margin_child(TTR("Add Extra Call Argument:"), add_bind_hb);
bind_editor = memnew(EditorInspector);
bind_editor->set_accessibility_name(TTRC("Extra Call Arguments"));
bind_controls.push_back(bind_editor);
vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true);
unbind_count = memnew(SpinBox);
unbind_count->set_tooltip_text(TTR("Allows to drop arguments sent by signal emitter."));
unbind_count->set_accessibility_name(TTRC("Unbind Signal Arguments"));
unbind_count->connect(SceneStringName(value_changed), callable_mp(this, &ConnectDialog::_unbind_count_changed));
vbc_right->add_margin_child(TTR("Unbind Signal Arguments:"), unbind_count);
@ -860,14 +874,14 @@ ConnectDialog::ConnectDialog() {
vbc_left->add_margin_child(TTR("Receiver Method:"), hbc_method);
dst_method = memnew(LineEdit);
dst_method->set_accessibility_name(TTRC("Receiver Method"));
dst_method->set_h_size_flags(Control::SIZE_EXPAND_FILL);
dst_method->connect(SceneStringName(text_changed), callable_mp(method_tree, &Tree::deselect_all).unbind(1));
hbc_method->add_child(dst_method);
register_text_enter(dst_method);
open_method_tree = memnew(Button);
open_method_tree = memnew(Button(TTRC("Pick")));
hbc_method->add_child(open_method_tree);
open_method_tree->set_text("Pick");
open_method_tree->connect(SceneStringName(pressed), callable_mp(this, &ConnectDialog::_open_method_popup));
advanced = memnew(CheckButton(TTR("Advanced")));
@ -1287,11 +1301,14 @@ void ConnectionsDock::_slot_menu_about_to_popup() {
}
void ConnectionsDock::_tree_gui_input(const Ref<InputEvent> &p_event) {
TreeItem *item = nullptr;
Point2 item_pos;
const Ref<InputEventKey> &key = p_event;
if (key.is_valid() && key->is_pressed() && !key->is_echo()) {
if (ED_IS_SHORTCUT("connections_editor/disconnect", p_event)) {
TreeItem *item = tree->get_selected();
item = tree->get_selected();
if (item && _get_item_type(*item) == TREE_ITEM_TYPE_CONNECTION) {
Connection connection = item->get_metadata(0);
_disconnect(connection);
@ -1309,22 +1326,32 @@ void ConnectionsDock::_tree_gui_input(const Ref<InputEvent> &p_event) {
return;
}
}
if (key.is_valid() && key->is_pressed() && key->is_action("ui_menu", true)) {
item = tree->get_selected();
if (!item) {
return;
}
item_pos = tree->get_item_rect(item).position;
}
// Handle RMB press.
const Ref<InputEventMouseButton> &mb_event = p_event;
if (mb_event.is_valid() && mb_event->is_pressed() && mb_event->get_button_index() == MouseButton::RIGHT) {
TreeItem *item = tree->get_item_at_position(mb_event->get_position());
item = tree->get_item_at_position(mb_event->get_position());
if (!item) {
return;
}
item_pos = mb_event->get_position();
}
if (item) {
if (item->is_selectable(0)) {
// Update selection now, before `about_to_popup` signal. Needed for SIGNAL and CONNECTION context menus.
tree->set_selected(item);
}
Vector2 screen_position = tree->get_screen_position() + mb_event->get_position();
Vector2 screen_position = tree->get_screen_position() + item_pos;
switch (_get_item_type(*item)) {
case TREE_ITEM_TYPE_ROOT:
@ -1459,8 +1486,8 @@ void ConnectionsDock::update_tree() {
List<MethodInfo> base_signals;
base->get_script_signal_list(&base_signals);
HashSet<String> base_signal_names;
for (List<MethodInfo>::Element *F = base_signals.front(); F; F = F->next()) {
base_signal_names.insert(F->get().name);
for (const MethodInfo &signal : base_signals) {
base_signal_names.insert(signal.name);
}
for (List<MethodInfo>::Element *F = class_signals.front(); F; F = F->next()) {
if (base_signal_names.has(F->get().name)) {
@ -1597,11 +1624,13 @@ ConnectionsDock::ConnectionsDock() {
search_box = memnew(LineEdit);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->set_placeholder(TTR("Filter Signals"));
search_box->set_accessibility_name(TTRC("Filter Signals"));
search_box->set_clear_button_enabled(true);
search_box->connect(SceneStringName(text_changed), callable_mp(this, &ConnectionsDock::_filter_changed));
vbc->add_child(search_box);
tree = memnew(ConnectionsDockTree);
tree->set_accessibility_name(TTRC("Connections"));
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_columns(1);
tree->set_select_mode(Tree::SELECT_ROW);
@ -1612,6 +1641,7 @@ ConnectionsDock::ConnectionsDock() {
tree->set_allow_rmb_select(true);
connect_button = memnew(Button);
connect_button->set_accessibility_name(TTRC("Connect"));
HBoxContainer *hb = memnew(HBoxContainer);
vbc->add_child(hb);
hb->add_spacer();
@ -1658,6 +1688,3 @@ ConnectionsDock::ConnectionsDock() {
add_theme_constant_override("separation", 3 * EDSCALE);
}
ConnectionsDock::~ConnectionsDock() {
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CONNECTIONS_DIALOG_H
#define CONNECTIONS_DIALOG_H
#pragma once
#include "scene/gui/check_button.h"
#include "scene/gui/dialogs.h"
@ -269,7 +268,4 @@ public:
void update_tree();
ConnectionsDock();
~ConnectionsDock();
};
#endif // CONNECTIONS_DIALOG_H

View file

@ -83,8 +83,7 @@ void CreateDialog::_fill_type_list() {
EditorData &ed = EditorNode::get_editor_data();
for (List<StringName>::Element *I = complete_type_list.front(); I; I = I->next()) {
StringName type = I->get();
for (const StringName &type : complete_type_list) {
if (!_should_hide_type(type)) {
type_list.push_back(type);
@ -216,8 +215,7 @@ void CreateDialog::_update_search() {
float highest_score = 0.0f;
StringName best_match;
for (List<StringName>::Element *I = type_list.front(); I; I = I->next()) {
StringName candidate = I->get();
for (const StringName &candidate : type_list) {
if (empty_search || search_text.is_subsequence_ofn(candidate)) {
_add_type(candidate, ClassDB::class_exists(candidate) ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE);
@ -637,7 +635,7 @@ void CreateDialog::_favorite_activated() {
}
Variant CreateDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
TreeItem *ti = favorites->get_item_at_position(p_point);
TreeItem *ti = (p_point == Vector2(Math::INF, Math::INF)) ? favorites->get_selected() : favorites->get_item_at_position(p_point);
if (ti) {
Dictionary d;
d["type"] = "create_favorite_drag";
@ -669,13 +667,13 @@ bool CreateDialog::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
void CreateDialog::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
Dictionary d = p_data;
TreeItem *ti = favorites->get_item_at_position(p_point);
TreeItem *ti = (p_point == Vector2(Math::INF, Math::INF)) ? favorites->get_selected() : favorites->get_item_at_position(p_point);
if (!ti) {
return;
}
String drop_at = ti->get_text(0);
int ds = favorites->get_drop_section_at_position(p_point);
int ds = (p_point == Vector2(Math::INF, Math::INF)) ? favorites->get_drop_section_at_position(favorites->get_item_rect(ti).position) : favorites->get_drop_section_at_position(p_point);
int drop_idx = favorite_list.find(drop_at);
if (drop_idx < 0) {
@ -789,6 +787,7 @@ CreateDialog::CreateDialog() {
vsc->add_child(fav_vb);
favorites = memnew(Tree);
favorites->set_accessibility_name(TTRC("Favorites"));
favorites->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
favorites->set_hide_root(true);
favorites->set_hide_folding(true);
@ -806,6 +805,7 @@ CreateDialog::CreateDialog() {
rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
recent = memnew(ItemList);
recent->set_accessibility_name(TTRC("Recent"));
recent->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
rec_vb->add_margin_child(TTR("Recent:"), recent, true);
recent->set_allow_reselect(true);
@ -820,6 +820,7 @@ CreateDialog::CreateDialog() {
hsc->add_child(vbc);
search_box = memnew(LineEdit);
search_box->set_accessibility_name(TTRC("Search"));
search_box->set_clear_button_enabled(true);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->connect(SceneStringName(text_changed), callable_mp(this, &CreateDialog::_text_changed));
@ -831,11 +832,13 @@ CreateDialog::CreateDialog() {
favorite = memnew(Button);
favorite->set_toggle_mode(true);
favorite->set_tooltip_text(TTR("(Un)favorite selected item."));
favorite->set_accessibility_name(TTRC("(Un)favorite"));
favorite->connect(SceneStringName(pressed), callable_mp(this, &CreateDialog::_favorite_toggled));
search_hb->add_child(favorite);
vbc->add_margin_child(TTR("Search:"), search_hb);
search_options = memnew(Tree);
search_options->set_accessibility_name(TTRC("Matches"));
search_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
search_options->connect("item_activated", callable_mp(this, &CreateDialog::_confirmed));
search_options->connect("cell_selected", callable_mp(this, &CreateDialog::_item_selected));
@ -843,6 +846,7 @@ CreateDialog::CreateDialog() {
vbc->add_margin_child(TTR("Matches:"), search_options, true);
help_bit = memnew(EditorHelpBit);
help_bit->set_accessibility_name(TTRC("Description"));
help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
help_bit->connect("request_hide", callable_mp(this, &CreateDialog::_hide_requested));
vbc->add_margin_child(TTR("Description:"), help_bit);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef CREATE_DIALOG_H
#define CREATE_DIALOG_H
#pragma once
#include "editor/editor_help.h"
#include "scene/gui/button.h"
@ -124,5 +123,3 @@ public:
CreateDialog();
};
#endif // CREATE_DIALOG_H

View file

@ -144,9 +144,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
// Send all current breakpoints
List<String> breakpoints;
ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
String breakpoint = E->get();
for (const String &breakpoint : breakpoints) {
String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://"
int line = breakpoint.substr(path.size()).to_int();
@ -301,12 +299,11 @@ Dictionary DebugAdapterParser::req_threads(const Dictionary &p_params) const {
Dictionary response = prepare_success_response(p_params), body;
response["body"] = body;
Array arr;
DAP::Thread thread;
thread.id = 1; // Hardcoded because Godot only supports debugging one thread at the moment
thread.name = "Main";
arr.push_back(thread.to_json());
Array arr = { thread.to_json() };
body["threads"] = arr;
return response;
@ -325,8 +322,7 @@ Dictionary DebugAdapterParser::req_stackTrace(const Dictionary &p_params) const
Array arr;
DebugAdapterProtocol *dap = DebugAdapterProtocol::get_singleton();
for (const KeyValue<DAP::StackFrame, List<int>> &E : dap->stackframe_list) {
DAP::StackFrame sf = E.key;
for (DAP::StackFrame sf : dap->stackframe_list) {
if (!lines_at_one) {
sf.line--;
}
@ -360,7 +356,7 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) co
// If path contains \, it's a Windows path, so we need to convert it to /, and make the drive letter uppercase
if (source.path.contains_char('\\')) {
source.path = source.path.replace("\\", "/");
source.path = source.path.replace_char('\\', '/');
source.path = source.path.substr(0, 1).to_upper() + source.path.substr(1);
}
@ -372,6 +368,8 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) co
lines.push_back(breakpoint.line + !lines_at_one);
}
// Always update the source checksum for the requested path, as it might have been modified externally.
DebugAdapterProtocol::get_singleton()->update_source(source.path);
Array updated_breakpoints = DebugAdapterProtocol::get_singleton()->update_breakpoints(source.path, lines);
body["breakpoints"] = updated_breakpoints;
@ -383,13 +381,12 @@ Dictionary DebugAdapterParser::req_breakpointLocations(const Dictionary &p_param
response["body"] = body;
Dictionary args = p_params["arguments"];
Array locations;
DAP::BreakpointLocation location;
location.line = args["line"];
if (args.has("endLine")) {
location.endLine = args["endLine"];
}
locations.push_back(location.to_json());
Array locations = { location.to_json() };
body["breakpoints"] = locations;
return response;
@ -403,15 +400,13 @@ Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const {
int frame_id = args["frameId"];
Array scope_list;
DAP::StackFrame frame;
frame.id = frame_id;
HashMap<DAP::StackFrame, List<int>, DAP::StackFrame>::Iterator E = DebugAdapterProtocol::get_singleton()->stackframe_list.find(frame);
HashMap<DebugAdapterProtocol::DAPStackFrameID, Vector<int>>::Iterator E = DebugAdapterProtocol::get_singleton()->scope_list.find(frame_id);
if (E) {
ERR_FAIL_COND_V(E->value.size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN));
List<int>::ConstIterator itr = E->value.begin();
for (int i = 0; i < 3; ++itr, ++i) {
const Vector<int> &scope_ids = E->value;
ERR_FAIL_COND_V(scope_ids.size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN));
for (int i = 0; i < 3; ++i) {
DAP::Scope scope;
scope.variablesReference = *itr;
scope.variablesReference = scope_ids[i];
switch (i) {
case 0:
scope.name = "Locals";
@ -595,8 +590,7 @@ Dictionary DebugAdapterParser::ev_stopped_breakpoint(const int &p_id) const {
body["reason"] = "breakpoint";
body["description"] = "Breakpoint";
Array breakpoints;
breakpoints.push_back(p_id);
Array breakpoints = { p_id };
body["hitBreakpointIds"] = breakpoints;
return event;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_PARSER_H
#define DEBUG_ADAPTER_PARSER_H
#pragma once
#include "core/config/project_settings.h"
#include "core/debugger/remote_debugger.h"
@ -49,7 +48,7 @@ private:
// If path contains \, it's a Windows path, so we need to convert it to /, and check as case-insensitive.
if (p_path.contains_char('\\')) {
String project_path = ProjectSettings::get_singleton()->get_resource_path();
String path = p_path.replace("\\", "/");
String path = p_path.replace_char('\\', '/');
return path.containsn(project_path);
}
return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path());
@ -103,5 +102,3 @@ public:
Dictionary ev_custom_data(const String &p_msg, const Array &p_data) const;
Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const;
};
#endif // DEBUG_ADAPTER_PARSER_H

View file

@ -66,8 +66,7 @@ Error DAPeer::handle_data() {
// End of headers
if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
r[l - 3] = '\0'; // Null terminate to read string
String header;
header.parse_utf8(r);
String header = String::utf8(r);
content_length = header.substr(16).to_int();
has_header = true;
req_pos = 0;
@ -93,8 +92,7 @@ Error DAPeer::handle_data() {
}
// Parse data
String msg;
msg.parse_utf8((const char *)req_buf, req_pos);
String msg = String::utf8((const char *)req_buf, req_pos);
// Apply a timestamp if it there's none yet
if (!timestamp) {
@ -118,12 +116,12 @@ Error DAPeer::send_data() {
if (!data.has("seq")) {
data["seq"] = ++seq;
}
String formatted_data = format_output(data);
const Vector<uint8_t> &formatted_data = format_output(data);
int data_sent = 0;
while (data_sent < formatted_data.length()) {
while (data_sent < formatted_data.size()) {
int curr_sent = 0;
Error err = connection->put_partial_data((const uint8_t *)formatted_data.utf8().get_data(), formatted_data.size() - data_sent - 1, curr_sent);
Error err = connection->put_partial_data(formatted_data.ptr() + data_sent, formatted_data.size() - data_sent, curr_sent);
if (err != OK) {
return err;
}
@ -134,15 +132,12 @@ Error DAPeer::send_data() {
return OK;
}
String DAPeer::format_output(const Dictionary &p_params) const {
String response = Variant(p_params).to_json_string();
String header = "Content-Length: ";
CharString charstr = response.utf8();
size_t len = charstr.length();
header += itos(len);
header += "\r\n\r\n";
Vector<uint8_t> DAPeer::format_output(const Dictionary &p_params) const {
const Vector<uint8_t> &content = Variant(p_params).to_json_string().to_utf8_buffer();
Vector<uint8_t> response = vformat("Content-Length: %d\r\n\r\n", content.size()).to_utf8_buffer();
return header + response;
response.append_array(content);
return response;
}
Error DebugAdapterProtocol::on_client_connected() {
@ -176,6 +171,7 @@ void DebugAdapterProtocol::reset_current_info() {
void DebugAdapterProtocol::reset_ids() {
breakpoint_id = 0;
breakpoint_list.clear();
breakpoint_source_list.clear();
reset_stack_info();
}
@ -185,6 +181,7 @@ void DebugAdapterProtocol::reset_stack_info() {
variable_id = 1;
stackframe_list.clear();
scope_list.clear();
variable_list.clear();
object_list.clear();
object_pending_set.clear();
@ -205,9 +202,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
x.value = rtos(vec.x);
y.value = rtos(vec.y);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
Array arr = { x.to_json(), y.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -230,11 +225,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
w.value = rtos(rect.size.x);
h.value = rtos(rect.size.y);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(w.to_json());
arr.push_back(h.to_json());
Array arr = { x.to_json(), y.to_json(), w.to_json(), h.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -254,10 +245,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
y.value = rtos(vec.y);
z.value = rtos(vec.z);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(z.to_json());
Array arr = { x.to_json(), y.to_json(), z.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -279,10 +267,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
y.variablesReference = parse_variant(transform.columns[1]);
origin.variablesReference = parse_variant(transform.columns[2]);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(origin.to_json());
Array arr = { x.to_json(), y.to_json(), origin.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -298,9 +283,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
normal.value = plane.normal;
normal.variablesReference = parse_variant(plane.normal);
Array arr;
arr.push_back(d.to_json());
arr.push_back(normal.to_json());
Array arr = { d.to_json(), normal.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -322,11 +305,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
z.value = rtos(quat.z);
w.value = rtos(quat.w);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(z.to_json());
arr.push_back(w.to_json());
Array arr = { x.to_json(), y.to_json(), z.to_json(), w.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -344,9 +323,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
position.variablesReference = parse_variant(aabb.position);
size.variablesReference = parse_variant(aabb.size);
Array arr;
arr.push_back(position.to_json());
arr.push_back(size.to_json());
Array arr = { position.to_json(), size.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -368,10 +345,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
y.variablesReference = parse_variant(basis.rows[1]);
z.variablesReference = parse_variant(basis.rows[2]);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(z.to_json());
Array arr = { x.to_json(), y.to_json(), z.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -388,9 +362,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
basis.variablesReference = parse_variant(transform.basis);
origin.variablesReference = parse_variant(transform.origin);
Array arr;
arr.push_back(basis.to_json());
arr.push_back(origin.to_json());
Array arr = { basis.to_json(), origin.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -412,11 +384,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
b.value = rtos(color.b);
a.value = rtos(color.a);
Array arr;
arr.push_back(r.to_json());
arr.push_back(g.to_json());
arr.push_back(b.to_json());
arr.push_back(a.to_json());
Array arr = { r.to_json(), g.to_json(), b.to_json(), a.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -428,8 +396,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -447,10 +414,10 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
Dictionary dictionary = p_var;
Array arr;
for (int i = 0; i < dictionary.size(); i++) {
for (const KeyValue<Variant, Variant> &kv : dictionary) {
DAP::Variable var;
var.name = dictionary.get_key_at_index(i);
Variant value = dictionary.get_value_at_index(i);
var.name = kv.key;
Variant value = kv.value;
var.type = Variant::get_type_name(value.get_type());
var.value = value;
var.variablesReference = parse_variant(value);
@ -467,8 +434,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -488,8 +454,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -509,8 +474,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -530,8 +494,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -551,8 +514,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -572,8 +534,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -593,8 +554,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -615,8 +575,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -637,8 +596,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -843,7 +801,9 @@ bool DebugAdapterProtocol::request_remote_object(const ObjectID &p_object_id) {
return false;
}
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_object(p_object_id);
TypedArray<uint64_t> arr;
arr.append(p_object_id);
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_objects(arr);
object_pending_set.insert(p_object_id);
return true;
@ -861,6 +821,30 @@ bool DebugAdapterProtocol::request_remote_evaluate(const String &p_eval, int p_s
return true;
}
const DAP::Source &DebugAdapterProtocol::fetch_source(const String &p_path) {
const String &global_path = ProjectSettings::get_singleton()->globalize_path(p_path);
HashMap<String, DAP::Source>::Iterator E = breakpoint_source_list.find(global_path);
if (E != breakpoint_source_list.end()) {
return E->value;
}
DAP::Source &added_source = breakpoint_source_list.insert(global_path, DAP::Source())->value;
added_source.name = global_path.get_file();
added_source.path = global_path;
added_source.compute_checksums();
return added_source;
}
void DebugAdapterProtocol::update_source(const String &p_path) {
const String &global_path = ProjectSettings::get_singleton()->globalize_path(p_path);
HashMap<String, DAP::Source>::Iterator E = breakpoint_source_list.find(global_path);
if (E != breakpoint_source_list.end()) {
E->value.compute_checksums();
}
}
bool DebugAdapterProtocol::process_message(const String &p_text) {
JSON json;
ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!");
@ -878,8 +862,7 @@ bool DebugAdapterProtocol::process_message(const String &p_text) {
if (parser->has_method(command)) {
_current_request = params["command"];
Array args;
args.push_back(params);
Array args = { params };
Dictionary response = parser->callv(command, args);
if (!response.is_empty()) {
_current_peer->res_queue.push_front(response);
@ -904,66 +887,66 @@ void DebugAdapterProtocol::notify_process() {
String launch_mode = _current_peer->attached ? "attach" : "launch";
Dictionary event = parser->ev_process(launch_mode);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_terminated() {
Dictionary event = parser->ev_terminated();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) {
for (const Ref<DAPeer> &peer : clients) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_exited(const int &p_exitcode) {
Dictionary event = parser->ev_exited(p_exitcode);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) {
for (const Ref<DAPeer> &peer : clients) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_paused() {
Dictionary event = parser->ev_stopped_paused();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_exception(const String &p_error) {
Dictionary event = parser->ev_stopped_exception(p_error);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_breakpoint(const int &p_id) {
Dictionary event = parser->ev_stopped_breakpoint(p_id);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_step() {
Dictionary event = parser->ev_stopped_step();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_continued() {
Dictionary event = parser->ev_continued();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if (_current_request == "continue" && E->get() == _current_peer) {
for (const Ref<DAPeer> &peer : clients) {
if (_current_request == "continue" && peer == _current_peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
reset_stack_info();
@ -971,15 +954,14 @@ void DebugAdapterProtocol::notify_continued() {
void DebugAdapterProtocol::notify_output(const String &p_message, RemoteDebugger::MessageType p_type) {
Dictionary event = parser->ev_output(p_message, p_type);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &p_data) {
Dictionary event = parser->ev_custom_data(p_msg, p_data);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
Ref<DAPeer> peer = E->get();
for (const Ref<DAPeer> &peer : clients) {
if (peer->supportsCustomData) {
peer->res_queue.push_back(event);
}
@ -988,11 +970,11 @@ void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &
void DebugAdapterProtocol::notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) {
Dictionary event = parser->ev_breakpoint(p_breakpoint, p_enabled);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if (_current_request == "setBreakpoints" && E->get() == _current_peer) {
for (const Ref<DAPeer> &peer : clients) {
if (_current_request == "setBreakpoints" && peer == _current_peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
}
@ -1001,23 +983,40 @@ Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array
// Add breakpoints
for (int i = 0; i < p_lines.size(); i++) {
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true);
DAP::Breakpoint breakpoint;
DAP::Breakpoint breakpoint(fetch_source(p_path));
breakpoint.line = p_lines[i];
breakpoint.source.path = p_path;
ERR_FAIL_COND_V(!breakpoint_list.find(breakpoint), Array());
updated_breakpoints.push_back(breakpoint_list.find(breakpoint)->get().to_json());
// Avoid duplicated entries.
List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint);
if (E) {
updated_breakpoints.push_back(E->get().to_json());
continue;
}
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true);
// Breakpoints are inserted at the end of the breakpoint list.
List<DAP::Breakpoint>::Element *added_breakpoint = breakpoint_list.back();
ERR_FAIL_NULL_V(added_breakpoint, Array());
ERR_FAIL_COND_V(!(added_breakpoint->get() == breakpoint), Array());
updated_breakpoints.push_back(added_breakpoint->get().to_json());
}
// Remove breakpoints
for (List<DAP::Breakpoint>::Element *E = breakpoint_list.front(); E; E = E->next()) {
DAP::Breakpoint b = E->get();
if (b.source.path == p_path && !p_lines.has(b.line)) {
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, b.line, false);
// Must be deferred because we are iterating the breakpoint list.
Vector<int> to_remove;
for (const DAP::Breakpoint &b : breakpoint_list) {
if (b.source->path == p_path && !p_lines.has(b.line)) {
to_remove.push_back(b.line);
}
}
// Safe to remove queued data now.
for (const int &line : to_remove) {
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, line, false);
}
return updated_breakpoints;
}
@ -1032,6 +1031,7 @@ void DebugAdapterProtocol::on_debug_paused() {
void DebugAdapterProtocol::on_debug_stopped() {
notify_exited();
notify_terminated();
reset_ids();
}
void DebugAdapterProtocol::on_debug_output(const String &p_message, int p_type) {
@ -1059,10 +1059,8 @@ void DebugAdapterProtocol::on_debug_breaked(const bool &p_reallydid, const bool
}
void DebugAdapterProtocol::on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled) {
DAP::Breakpoint breakpoint;
DAP::Breakpoint breakpoint(fetch_source(p_path));
breakpoint.verified = true;
breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(p_path);
breakpoint.source.compute_checksums();
breakpoint.line = p_line;
if (p_enabled) {
@ -1085,8 +1083,7 @@ void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) {
if (_processing_breakpoint && !p_stack_dump.is_empty()) {
// Find existing breakpoint
Dictionary d = p_stack_dump[0];
DAP::Breakpoint breakpoint;
breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(d["file"]);
DAP::Breakpoint breakpoint(fetch_source(d["file"]));
breakpoint.line = d["line"];
List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint);
@ -1099,25 +1096,26 @@ void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) {
stackframe_id = 0;
stackframe_list.clear();
scope_list.clear();
// Fill in stacktrace information
for (int i = 0; i < p_stack_dump.size(); i++) {
Dictionary stack_info = p_stack_dump[i];
DAP::StackFrame stackframe;
DAP::StackFrame stackframe(fetch_source(stack_info["file"]));
stackframe.id = stackframe_id++;
stackframe.name = stack_info["function"];
stackframe.line = stack_info["line"];
stackframe.column = 0;
stackframe.source.path = ProjectSettings::get_singleton()->globalize_path(stack_info["file"]);
stackframe.source.compute_checksums();
// Information for "Locals", "Members" and "Globals" variables respectively
List<int> scope_ids;
Vector<int> scope_ids;
for (int j = 0; j < 3; j++) {
scope_ids.push_back(variable_id++);
}
stackframe_list.insert(stackframe, scope_ids);
stackframe_list.push_back(stackframe);
scope_list.insert(stackframe.id, scope_ids);
}
_current_frame = 0;
@ -1126,12 +1124,9 @@ void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) {
void DebugAdapterProtocol::on_debug_stack_frame_vars(const int &p_size) {
_remaining_vars = p_size;
DAP::StackFrame frame;
frame.id = _current_frame;
ERR_FAIL_COND(!stackframe_list.has(frame));
List<int> scope_ids = stackframe_list.find(frame)->value;
for (List<int>::Element *E = scope_ids.front(); E; E = E->next()) {
int var_id = E->get();
ERR_FAIL_COND(!scope_list.has(_current_frame));
Vector<int> scope_ids = scope_list.find(_current_frame)->value;
for (const int &var_id : scope_ids) {
if (variable_list.has(var_id)) {
variable_list.find(var_id)->value.clear();
} else {
@ -1144,11 +1139,9 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) {
DebuggerMarshalls::ScriptStackVariable stack_var;
stack_var.deserialize(p_data);
ERR_FAIL_COND(stackframe_list.is_empty());
DAP::StackFrame frame;
frame.id = _current_frame;
ERR_FAIL_COND(!scope_list.has(_current_frame));
Vector<int> scope_ids = scope_list.find(_current_frame)->value;
List<int> scope_ids = stackframe_list.find(frame)->value;
ERR_FAIL_COND(scope_ids.size() != 3);
ERR_FAIL_INDEX(stack_var.type, 4);
int var_id = scope_ids.get(stack_var.type);
@ -1192,8 +1185,7 @@ void DebugAdapterProtocol::poll() {
on_client_connected();
}
List<Ref<DAPeer>> to_delete;
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
Ref<DAPeer> peer = E->get();
for (const Ref<DAPeer> &peer : clients) {
peer->connection->poll();
StreamPeerTCP::Status status = peer->connection->get_status();
if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) {
@ -1211,8 +1203,8 @@ void DebugAdapterProtocol::poll() {
}
}
for (List<Ref<DAPeer>>::Element *E = to_delete.front(); E; E = E->next()) {
on_client_disconnected(E->get());
for (const Ref<DAPeer> &peer : to_delete) {
on_client_disconnected(peer);
}
to_delete.clear();
}
@ -1225,8 +1217,8 @@ Error DebugAdapterProtocol::start(int p_port, const IPAddress &p_bind_ip) {
}
void DebugAdapterProtocol::stop() {
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->connection->disconnect_from_host();
for (const Ref<DAPeer> &peer : clients) {
peer->connection->disconnect_from_host();
}
clients.clear();

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_PROTOCOL_H
#define DEBUG_ADAPTER_PROTOCOL_H
#pragma once
#include "core/debugger/debugger_marshalls.h"
#include "core/io/stream_peer_tcp.h"
@ -68,7 +67,7 @@ struct DAPeer : RefCounted {
Error handle_data();
Error send_data();
String format_output(const Dictionary &p_params) const;
Vector<uint8_t> format_output(const Dictionary &p_params) const;
};
class DebugAdapterProtocol : public Object {
@ -77,6 +76,7 @@ class DebugAdapterProtocol : public Object {
friend class DebugAdapterParser;
using DAPVarID = int;
using DAPStackFrameID = int;
private:
static DebugAdapterProtocol *singleton;
@ -110,6 +110,9 @@ private:
bool request_remote_object(const ObjectID &p_object_id);
bool request_remote_evaluate(const String &p_eval, int p_stack_frame);
const DAP::Source &fetch_source(const String &p_path);
void update_source(const String &p_path);
bool _initialized = false;
bool _processing_breakpoint = false;
bool _stepping = false;
@ -126,7 +129,9 @@ private:
int stackframe_id = 0;
DAPVarID variable_id = 0;
List<DAP::Breakpoint> breakpoint_list;
HashMap<DAP::StackFrame, List<int>, DAP::StackFrame> stackframe_list;
HashMap<String, DAP::Source> breakpoint_source_list;
List<DAP::StackFrame> stackframe_list;
HashMap<DAPStackFrameID, Vector<int>> scope_list;
HashMap<DAPVarID, Array> variable_list;
HashMap<ObjectID, DAPVarID> object_list;
@ -168,5 +173,3 @@ public:
DebugAdapterProtocol();
~DebugAdapterProtocol();
};
#endif // DEBUG_ADAPTER_PROTOCOL_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_SERVER_H
#define DEBUG_ADAPTER_SERVER_H
#pragma once
#include "debug_adapter_protocol.h"
#include "editor/plugins/editor_plugin.h"
@ -54,5 +53,3 @@ public:
void start();
void stop();
};
#endif // DEBUG_ADAPTER_SERVER_H

View file

@ -28,11 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_TYPES_H
#define DEBUG_ADAPTER_TYPES_H
#pragma once
#include "core/io/json.h"
#include "core/variant/dictionary.h"
#include "core/io/file_access.h"
namespace DAP {
@ -69,6 +67,8 @@ public:
void compute_checksums() {
ERR_FAIL_COND(path.is_empty());
_checksums.clear();
// MD5
Checksum md5;
md5.algorithm = "MD5";
@ -102,18 +102,24 @@ public:
struct Breakpoint {
int id = 0;
bool verified = false;
Source source;
const Source *source = nullptr;
int line = 0;
Breakpoint() = default; // Empty constructor is invalid, but is necessary because Godot's collections don't support rvalues.
Breakpoint(const Source &p_source) :
source(&p_source) {}
bool operator==(const Breakpoint &p_other) const {
return source.path == p_other.source.path && line == p_other.line;
return source == p_other.source && line == p_other.line;
}
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["id"] = id;
dict["verified"] = verified;
dict["source"] = source.to_json();
if (source) {
dict["source"] = source->to_json();
}
dict["line"] = line;
return dict;
@ -159,9 +165,7 @@ struct Capabilities {
dict["supportsTerminateRequest"] = supportsTerminateRequest;
dict["supportsBreakpointLocationsRequest"] = supportsBreakpointLocationsRequest;
Array arr;
arr.push_back(supportedChecksumAlgorithms[0]);
arr.push_back(supportedChecksumAlgorithms[1]);
Array arr = { supportedChecksumAlgorithms[0], supportedChecksumAlgorithms[1] };
dict["supportedChecksumAlgorithms"] = arr;
return dict;
@ -215,30 +219,25 @@ struct SourceBreakpoint {
struct StackFrame {
int id = 0;
String name;
Source source;
const Source *source = nullptr;
int line = 0;
int column = 0;
StackFrame() = default; // Empty constructor is invalid, but is necessary because Godot's collections don't support rvalues.
StackFrame(const Source &p_source) :
source(&p_source) {}
static uint32_t hash(const StackFrame &p_frame) {
return hash_murmur3_one_32(p_frame.id);
}
bool operator==(const StackFrame &p_other) const {
return id == p_other.id;
}
_FORCE_INLINE_ void from_json(const Dictionary &p_params) {
id = p_params["id"];
name = p_params["name"];
source.from_json(p_params["source"]);
line = p_params["line"];
column = p_params["column"];
}
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["id"] = id;
dict["name"] = name;
dict["source"] = source.to_json();
if (source) {
dict["source"] = source->to_json();
}
dict["line"] = line;
dict["column"] = column;
@ -277,5 +276,3 @@ struct Variable {
};
} // namespace DAP
#endif // DEBUG_ADAPTER_TYPES_H

View file

@ -33,29 +33,57 @@
#include "core/debugger/debugger_marshalls.h"
#include "core/io/marshalls.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector_dock.h"
#include "scene/debugger/scene_debugger.h"
bool EditorDebuggerRemoteObject::_set(const StringName &p_name, const Variant &p_value) {
if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/")) {
bool EditorDebuggerRemoteObjects::_set(const StringName &p_name, const Variant &p_value) {
return _set_impl(p_name, p_value, "");
}
bool EditorDebuggerRemoteObjects::_set_impl(const StringName &p_name, const Variant &p_value, const String &p_field) {
String name = p_name;
if (name.begins_with("Metadata/")) {
name = name.replace_first("Metadata/", "metadata/");
}
if (!prop_values.has(name) || String(name).begins_with("Constants/")) {
return false;
}
prop_values[p_name] = p_value;
emit_signal(SNAME("value_edited"), remote_object_id, p_name, p_value);
Dictionary &values = prop_values[p_name];
Dictionary old_values = values.duplicate();
for (const uint64_t key : values.keys()) {
values.set(key, p_value);
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
const int size = remote_object_ids.size();
ur->create_action(size == 1 ? vformat(TTR("Set %s"), name) : vformat(TTR("Set %s on %d objects"), name, size), UndoRedo::MERGE_ENDS);
ur->add_do_method(this, SNAME("emit_signal"), SNAME("values_edited"), name, values, p_field);
ur->add_undo_method(this, SNAME("emit_signal"), SNAME("values_edited"), name, old_values, p_field);
ur->commit_action();
return true;
}
bool EditorDebuggerRemoteObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!prop_values.has(p_name)) {
bool EditorDebuggerRemoteObjects::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
if (name.begins_with("Metadata/")) {
name = name.replace_first("Metadata/", "metadata/");
}
if (!prop_values.has(name)) {
return false;
}
r_ret = prop_values[p_name];
r_ret = prop_values[p_name][remote_object_ids[0]];
return true;
}
void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->clear(); // Sorry, no want category.
void EditorDebuggerRemoteObjects::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->clear(); // Sorry, don't want any categories.
for (const PropertyInfo &prop : prop_list) {
if (prop.name == "script") {
// Skip the script property, it's always added by the non-virtual method.
@ -66,31 +94,35 @@ void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list)
}
}
String EditorDebuggerRemoteObject::get_title() {
if (remote_object_id.is_valid()) {
return vformat(TTR("Remote %s:"), String(type_name)) + " " + itos(remote_object_id);
} else {
return "<null>";
}
void EditorDebuggerRemoteObjects::set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field) {
_set_impl(p_property, p_value, p_field);
}
Variant EditorDebuggerRemoteObject::get_variant(const StringName &p_name) {
String EditorDebuggerRemoteObjects::get_title() {
if (!remote_object_ids.is_empty() && ObjectID(remote_object_ids[0].operator uint64_t()).is_valid()) {
const int size = remote_object_ids.size();
return size == 1 ? vformat(TTR("Remote %s: %d"), type_name, remote_object_ids[0]) : vformat(TTR("Remote %s (%d Selected)"), type_name, size);
}
return "<null>";
}
Variant EditorDebuggerRemoteObjects::get_variant(const StringName &p_name) {
Variant var;
_get(p_name, var);
return var;
}
void EditorDebuggerRemoteObject::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObject::get_title);
ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant);
ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear);
ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id);
void EditorDebuggerRemoteObjects::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObjects::get_title);
ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
ADD_SIGNAL(MethodInfo("values_edited", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::DICTIONARY, "values", PROPERTY_HINT_DICTIONARY_TYPE, "uint64_t:Variant"), PropertyInfo(Variant::STRING, "field")));
}
/// EditorDebuggerInspector
EditorDebuggerInspector::EditorDebuggerInspector() {
variables = memnew(EditorDebuggerRemoteObject);
variables = memnew(EditorDebuggerRemoteObjects);
}
EditorDebuggerInspector::~EditorDebuggerInspector() {
@ -100,7 +132,7 @@ EditorDebuggerInspector::~EditorDebuggerInspector() {
void EditorDebuggerInspector::_bind_methods() {
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("object_edited", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
ADD_SIGNAL(MethodInfo("objects_edited", PropertyInfo(Variant::ARRAY, "ids"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value"), PropertyInfo(Variant::STRING, "field")));
ADD_SIGNAL(MethodInfo("object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
}
@ -111,50 +143,143 @@ void EditorDebuggerInspector::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
variables->remote_object_ids.append(0);
edit(variables);
} break;
}
}
void EditorDebuggerInspector::_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) {
emit_signal(SNAME("object_edited"), p_id, p_prop, p_value);
void EditorDebuggerInspector::_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field) {
emit_signal(SNAME("objects_edited"), p_prop, p_values, p_field);
}
void EditorDebuggerInspector::_object_selected(ObjectID p_object) {
emit_signal(SNAME("object_selected"), p_object);
}
ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
EditorDebuggerRemoteObject *debug_obj = nullptr;
EditorDebuggerRemoteObjects *EditorDebuggerInspector::set_objects(const Array &p_arr) {
ERR_FAIL_COND_V(p_arr.is_empty(), nullptr);
SceneDebuggerObject obj;
obj.deserialize(p_arr);
ERR_FAIL_COND_V(obj.id.is_null(), ObjectID());
TypedArray<uint64_t> ids;
LocalVector<SceneDebuggerObject> objects;
for (const Array arr : p_arr) {
SceneDebuggerObject obj;
obj.deserialize(arr);
if (obj.id.is_valid()) {
ids.push_back((uint64_t)obj.id);
objects.push_back(obj);
}
}
ERR_FAIL_COND_V(ids.is_empty(), nullptr);
if (remote_objects.has(obj.id)) {
debug_obj = remote_objects[obj.id];
} else {
debug_obj = memnew(EditorDebuggerRemoteObject);
debug_obj->remote_object_id = obj.id;
debug_obj->type_name = obj.class_name;
remote_objects[obj.id] = debug_obj;
debug_obj->connect("value_edited", callable_mp(this, &EditorDebuggerInspector::_object_edited));
// Sorting is necessary, as selected nodes in the remote tree are ordered by index.
ids.sort();
EditorDebuggerRemoteObjects *remote_objects = nullptr;
for (EditorDebuggerRemoteObjects *robjs : remote_objects_list) {
if (robjs->remote_object_ids == ids) {
remote_objects = robjs;
break;
}
}
int old_prop_size = debug_obj->prop_list.size();
if (!remote_objects) {
remote_objects = memnew(EditorDebuggerRemoteObjects);
remote_objects->remote_object_ids = ids;
remote_objects->remote_object_ids.make_read_only();
remote_objects->connect("values_edited", callable_mp(this, &EditorDebuggerInspector::_objects_edited));
remote_objects_list.push_back(remote_objects);
}
debug_obj->prop_list.clear();
StringName class_name = objects[0].class_name;
if (class_name != SNAME("Object")) {
// Search for the common class between all selected objects.
bool check_type_again = true;
while (check_type_again) {
check_type_again = false;
if (class_name == SNAME("Object") || class_name == StringName()) {
// All objects inherit from Object, so no need to continue checking.
class_name = SNAME("Object");
break;
}
// Check that all objects inherit from type_name.
for (const SceneDebuggerObject &obj : objects) {
if (obj.class_name == class_name || ClassDB::is_parent_class(obj.class_name, class_name)) {
continue; // class_name is the same or a parent of the object's class.
}
// class_name is not a parent of the node's class, so check again with the parent class.
class_name = ClassDB::get_parent_class(class_name);
check_type_again = true;
break;
}
}
}
remote_objects->type_name = class_name;
// Search for properties that are present in all selected objects.
struct UsageData {
int qty = 0;
SceneDebuggerObject::SceneDebuggerProperty prop;
TypedDictionary<uint64_t, Variant> values;
};
HashMap<String, UsageData> usage;
int nc = 0;
for (const SceneDebuggerObject &obj : objects) {
for (const SceneDebuggerObject::SceneDebuggerProperty &prop : obj.properties) {
PropertyInfo pinfo = prop.first;
if (pinfo.name == "script") {
continue; // Added later manually, since this is intercepted before being set (check Variant Object::get()).
} else if (pinfo.name.begins_with("metadata/")) {
pinfo.name = pinfo.name.replace_first("metadata/", "Metadata/"); // Trick to not get actual metadata edited from EditorDebuggerRemoteObjects.
}
if (!usage.has(pinfo.name)) {
UsageData usage_dt;
usage_dt.prop = prop;
usage_dt.prop.first.name = pinfo.name;
usage_dt.values[obj.id] = prop.second;
usage[pinfo.name] = usage_dt;
}
// Make sure only properties with the same exact PropertyInfo data will appear.
if (usage[pinfo.name].prop.first == pinfo) {
usage[pinfo.name].qty++;
usage[pinfo.name].values[obj.id] = prop.second;
}
}
nc++;
}
for (HashMap<String, UsageData>::Iterator E = usage.begin(); E;) {
HashMap<String, UsageData>::Iterator next = E;
++next;
UsageData usage_dt = E->value;
if (nc != usage_dt.qty) {
// Doesn't appear on all of them, remove it.
usage.erase(E->key);
}
E = next;
}
int old_prop_size = remote_objects->prop_list.size();
remote_objects->prop_list.clear();
int new_props_added = 0;
HashSet<String> changed;
for (SceneDebuggerObject::SceneDebuggerProperty &property : obj.properties) {
PropertyInfo &pinfo = property.first;
Variant &var = property.second;
for (KeyValue<String, UsageData> &KV : usage) {
const PropertyInfo &pinfo = KV.value.prop.first;
Variant var = KV.value.values[remote_objects->remote_object_ids[0]];
if (pinfo.type == Variant::OBJECT) {
if (var.is_string()) {
String path = var;
if (path.contains("::")) {
// built-in resource
// Built-in resource.
String base_path = path.get_slice("::", 0);
Ref<Resource> dependency = ResourceLoader::load(base_path);
if (dependency.is_valid()) {
@ -162,15 +287,16 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
}
}
var = ResourceLoader::load(path);
KV.value.values[remote_objects->remote_object_ids[0]] = var;
if (pinfo.hint_string == "Script") {
if (debug_obj->get_script() != var) {
debug_obj->set_script(Ref<RefCounted>());
if (remote_objects->get_script() != var) {
remote_objects->set_script(Ref<RefCounted>());
Ref<Script> scr(var);
if (scr.is_valid()) {
ScriptInstance *scr_instance = scr->placeholder_instance_create(debug_obj);
ScriptInstance *scr_instance = scr->placeholder_instance_create(remote_objects);
if (scr_instance) {
debug_obj->set_script_and_instance(var, scr_instance);
remote_objects->set_script_and_instance(var, scr_instance);
}
}
}
@ -178,49 +304,67 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
}
}
//always add the property, since props may have been added or removed
debug_obj->prop_list.push_back(pinfo);
// Always add the property, since props may have been added or removed.
remote_objects->prop_list.push_back(pinfo);
if (!debug_obj->prop_values.has(pinfo.name)) {
if (!remote_objects->prop_values.has(pinfo.name)) {
new_props_added++;
debug_obj->prop_values[pinfo.name] = var;
} else {
if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debug_obj->prop_values[pinfo.name], var))) {
debug_obj->prop_values[pinfo.name] = var;
changed.insert(pinfo.name);
}
} else if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, remote_objects->prop_values[pinfo.name], var))) {
changed.insert(pinfo.name);
}
remote_objects->prop_values[pinfo.name] = KV.value.values;
}
if (old_prop_size == debug_obj->prop_list.size() && new_props_added == 0) {
//only some may have changed, if so, then update those, if exist
if (old_prop_size == remote_objects->prop_list.size() && new_props_added == 0) {
// Only some may have changed, if so, then update those, if they exist.
for (const String &E : changed) {
emit_signal(SNAME("object_property_updated"), debug_obj->remote_object_id, E);
emit_signal(SNAME("object_property_updated"), remote_objects->get_instance_id(), E);
}
} else {
//full update, because props were added or removed
debug_obj->update();
// Full update, because props were added or removed.
remote_objects->update();
}
return remote_objects;
}
void EditorDebuggerInspector::clear_remote_inspector() {
if (remote_objects_list.is_empty()) {
return;
}
const Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();
// Check if the inspector holds remote items, and take it out if so.
if (Object::cast_to<EditorDebuggerRemoteObjects>(obj)) {
EditorNode::get_singleton()->push_item(nullptr);
}
return obj.id;
}
void EditorDebuggerInspector::clear_cache() {
for (const KeyValue<ObjectID, EditorDebuggerRemoteObject *> &E : remote_objects) {
EditorNode *editor = EditorNode::get_singleton();
if (editor->get_editor_selection_history()->get_current() == E.value->get_instance_id()) {
editor->push_item(nullptr);
}
memdelete(E.value);
clear_remote_inspector();
for (EditorDebuggerRemoteObjects *robjs : remote_objects_list) {
memdelete(robjs);
}
remote_objects.clear();
remote_objects_list.clear();
remote_dependencies.clear();
}
Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
if (remote_objects.has(p_id)) {
return remote_objects[p_id];
void EditorDebuggerInspector::invalidate_selection_from_cache(const TypedArray<uint64_t> &p_ids) {
for (EditorDebuggerRemoteObjects *robjs : remote_objects_list) {
if (robjs->remote_object_ids == p_ids) {
const Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();
if (obj == robjs) {
EditorNode::get_singleton()->push_item(nullptr);
}
remote_objects_list.erase(robjs);
memdelete(robjs);
break;
}
}
return nullptr;
}
void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_offset) {
@ -270,7 +414,7 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_off
}
variables->prop_list.insert_before(current, pinfo);
}
variables->prop_values[type + n] = v;
variables->prop_values[type + n][0] = v;
variables->update();
edit(variables);
@ -288,9 +432,9 @@ void EditorDebuggerInspector::clear_stack_variables() {
}
String EditorDebuggerInspector::get_stack_variable(const String &p_var) {
for (KeyValue<StringName, Variant> &E : variables->prop_values) {
for (KeyValue<StringName, TypedDictionary<uint64_t, Variant>> &E : variables->prop_values) {
String v = E.key.operator String();
if (v.get_slice("/", 1) == p_var) {
if (v.get_slicec('/', 1) == p_var) {
return variables->get_variant(v);
}
}

View file

@ -28,13 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_INSPECTOR_H
#define EDITOR_DEBUGGER_INSPECTOR_H
#pragma once
#include "core/variant/typed_dictionary.h"
#include "editor/editor_inspector.h"
class EditorDebuggerRemoteObject : public Object {
GDCLASS(EditorDebuggerRemoteObject, Object);
class SceneDebuggerObject;
class EditorDebuggerRemoteObjects : public Object {
GDCLASS(EditorDebuggerRemoteObjects, Object);
private:
bool _set_impl(const StringName &p_name, const Variant &p_value, const String &p_field);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@ -43,14 +48,13 @@ protected:
static void _bind_methods();
public:
ObjectID remote_object_id;
TypedArray<uint64_t> remote_object_ids;
String type_name;
List<PropertyInfo> prop_list;
HashMap<StringName, Variant> prop_values;
HashMap<StringName, TypedDictionary<uint64_t, Variant>> prop_values;
ObjectID get_remote_object_id() { return remote_object_id; }
void set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field);
String get_title();
Variant get_variant(const StringName &p_name);
void clear() {
@ -59,21 +63,18 @@ public:
}
void update() { notify_property_list_changed(); }
EditorDebuggerRemoteObject() {}
};
class EditorDebuggerInspector : public EditorInspector {
GDCLASS(EditorDebuggerInspector, EditorInspector);
private:
ObjectID inspected_object_id;
HashMap<ObjectID, EditorDebuggerRemoteObject *> remote_objects;
LocalVector<EditorDebuggerRemoteObjects *> remote_objects_list;
HashSet<Ref<Resource>> remote_dependencies;
EditorDebuggerRemoteObject *variables = nullptr;
EditorDebuggerRemoteObjects *variables = nullptr;
void _object_selected(ObjectID p_object);
void _object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value);
void _objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field);
protected:
void _notification(int p_what);
@ -84,14 +85,13 @@ public:
~EditorDebuggerInspector();
// Remote Object cache
ObjectID add_object(const Array &p_arr);
Object *get_object(ObjectID p_id);
EditorDebuggerRemoteObjects *set_objects(const Array &p_array);
void clear_remote_inspector();
void clear_cache();
void invalidate_selection_from_cache(const TypedArray<uint64_t> &p_ids);
// Stack Dump variables
String get_stack_variable(const String &p_var);
void add_stack_variable(const Array &p_arr, int p_offset = -1);
void clear_stack_variables();
};
#endif // EDITOR_DEBUGGER_INSPECTOR_H

View file

@ -82,7 +82,8 @@ EditorDebuggerNode::EditorDebuggerNode() {
// Remote scene tree
remote_scene_tree = memnew(EditorDebuggerTree);
remote_scene_tree->connect("object_selected", callable_mp(this, &EditorDebuggerNode::_remote_object_requested));
remote_scene_tree->connect("objects_selected", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested));
remote_scene_tree->connect("selection_cleared", callable_mp(this, &EditorDebuggerNode::_remote_selection_cleared));
remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested));
remote_scene_tree->connect("button_clicked", callable_mp(this, &EditorDebuggerNode::_remote_tree_button_pressed));
SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree);
@ -109,11 +110,13 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));
node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));
node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id));
node->connect("debug_data", callable_mp(this, &EditorDebuggerNode::_debug_data).bind(id));
node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id));
node->connect("remote_tree_clear_selection_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_clear_selection_requested).bind(id));
node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id));
node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id));
node->connect("remote_objects_updated", callable_mp(this, &EditorDebuggerNode::_remote_objects_updated).bind(id));
node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));
node->connect("remote_object_requested", callable_mp(this, &EditorDebuggerNode::_remote_object_requested).bind(id));
node->connect("remote_objects_requested", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested).bind(id));
node->connect("set_breakpoint", callable_mp(this, &EditorDebuggerNode::_breakpoint_set_in_tree).bind(id));
node->connect("clear_breakpoints", callable_mp(this, &EditorDebuggerNode::_breakpoints_cleared_in_tree).bind(id));
node->connect("errors_cleared", callable_mp(this, &EditorDebuggerNode::_update_errors));
@ -222,10 +225,6 @@ void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {
p_undo_redo->set_property_notify_callback(_properties_changed, this);
}
EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_current()));
}
ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const {
return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(p_id));
}
@ -292,6 +291,10 @@ void EditorDebuggerNode::stop(bool p_force) {
if (keep_open && !p_force) {
return;
}
remote_scene_tree_wait = false;
inspect_edited_object_wait = false;
current_uri.clear();
if (server.is_valid()) {
server->stop();
@ -304,6 +307,7 @@ void EditorDebuggerNode::stop(bool p_force) {
server.unref();
}
// Also close all debugging sessions.
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
if (dbg->is_session_active()) {
@ -351,21 +355,29 @@ void EditorDebuggerNode::_notification(int p_what) {
_update_errors();
// Remote scene tree update
remote_scene_tree_timeout -= get_process_delta_time();
if (remote_scene_tree_timeout < 0) {
remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");
if (remote_scene_tree->is_visible_in_tree()) {
get_current_debugger()->request_remote_tree();
// Remote scene tree update.
if (!remote_scene_tree_wait) {
remote_scene_tree_timeout -= get_process_delta_time();
if (remote_scene_tree_timeout < 0) {
remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");
if (remote_scene_tree->is_visible_in_tree()) {
remote_scene_tree_wait = true;
get_current_debugger()->request_remote_tree();
}
}
}
// Remote inspector update
inspect_edited_object_timeout -= get_process_delta_time();
if (inspect_edited_object_timeout < 0) {
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
get_current_debugger()->request_remote_object(obj->remote_object_id);
// Remote inspector update.
if (!inspect_edited_object_wait) {
inspect_edited_object_timeout -= get_process_delta_time();
if (inspect_edited_object_timeout < 0) {
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
if (EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(InspectorDock::get_inspector_singleton()->get_edited_object())) {
inspect_edited_object_wait = true;
get_current_debugger()->request_remote_objects(robjs->remote_object_ids, false);
}
}
}
@ -467,21 +479,26 @@ void EditorDebuggerNode::_debugger_stopped(int p_id) {
void EditorDebuggerNode::_debugger_wants_stop(int p_id) {
// Ask editor to kill PID.
int pid = get_debugger(p_id)->get_remote_pid();
if (pid) {
if (int pid = get_debugger(p_id)->get_remote_pid()) {
callable_mp(EditorNode::get_singleton(), &EditorNode::stop_child_process).call_deferred(pid);
}
}
void EditorDebuggerNode::_debugger_changed(int p_tab) {
if (get_inspected_remote_object()) {
// Clear inspected object, you can only inspect objects in selected debugger.
// Hopefully, in the future, we will have one inspector per debugger.
EditorNode::get_singleton()->push_item(nullptr);
remote_scene_tree_wait = false;
inspect_edited_object_wait = false;
if (Object *robjs = InspectorDock::get_inspector_singleton()->get_edited_object()) {
if (Object::cast_to<EditorDebuggerRemoteObjects>(robjs)) {
// Clear inspected object, you can only inspect objects in selected debugger.
// Hopefully, in the future, we will have one inspector per debugger.
EditorNode::get_singleton()->push_item(nullptr);
}
}
if (get_previous_debugger()) {
_text_editor_stack_clear(get_previous_debugger());
if (ScriptEditorDebugger *prev_debug = get_previous_debugger()) {
prev_debug->clear_inspector();
_text_editor_stack_clear(prev_debug);
}
if (remote_scene_tree->is_visible_in_tree()) {
get_current_debugger()->request_remote_tree();
@ -493,6 +510,18 @@ void EditorDebuggerNode::_debugger_changed(int p_tab) {
_break_state_changed();
}
void EditorDebuggerNode::_debug_data(const String &p_msg, const Array &p_data, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
if (p_msg == "scene:scene_tree") {
remote_scene_tree_wait = false;
} else if (p_msg == "scene:inspect_objects") {
inspect_edited_object_wait = false;
}
}
void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) {
script_menu = p_button;
script_menu->set_text(TTR("Debug"));
@ -587,6 +616,10 @@ bool EditorDebuggerNode::is_skip_breakpoints() const {
return get_current_debugger()->is_skip_breakpoints();
}
bool EditorDebuggerNode::is_ignore_error_breaks() const {
return get_default_debugger()->is_ignore_error_breaks();
}
void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
breakpoints[Breakpoint(p_path, p_line)] = p_enabled;
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
@ -646,11 +679,39 @@ void EditorDebuggerNode::request_remote_tree() {
get_current_debugger()->request_remote_tree();
}
void EditorDebuggerNode::_remote_tree_select_requested(ObjectID p_id, int p_debugger) {
void EditorDebuggerNode::set_remote_selection(const TypedArray<int64_t> &p_ids) {
stop_waiting_inspection();
get_current_debugger()->request_remote_objects(p_ids);
}
void EditorDebuggerNode::clear_remote_tree_selection() {
remote_scene_tree->clear_selection();
get_current_debugger()->clear_inspector(remote_scene_tree_clear_msg);
}
void EditorDebuggerNode::stop_waiting_inspection() {
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
inspect_edited_object_wait = false;
}
bool EditorDebuggerNode::match_remote_selection(const TypedArray<uint64_t> &p_ids) const {
return p_ids == remote_scene_tree->get_selection();
}
void EditorDebuggerNode::_remote_tree_select_requested(const TypedArray<int64_t> &p_ids, int p_debugger) {
if (p_debugger == tabs->get_current_tab()) {
remote_scene_tree->select_nodes(p_ids);
}
}
void EditorDebuggerNode::_remote_tree_clear_selection_requested(int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
remote_scene_tree->select_node(p_id);
remote_scene_tree->clear_selection();
remote_scene_tree_clear_msg = false;
get_current_debugger()->clear_inspector(false);
remote_scene_tree_clear_msg = true;
}
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
@ -679,37 +740,37 @@ void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_colum
}
}
void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
void EditorDebuggerNode::_remote_objects_updated(EditorDebuggerRemoteObjects *p_objs, int p_debugger) {
if (p_debugger == tabs->get_current_tab() && p_objs != InspectorDock::get_inspector_singleton()->get_edited_object()) {
EditorNode::get_singleton()->push_item(p_objs);
}
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
if (obj->remote_object_id == p_id) {
return; // Already being edited
}
}
EditorNode::get_singleton()->push_item(get_current_debugger()->get_remote_object(p_id));
}
void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
if (obj->remote_object_id != p_id) {
return;
}
Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();
if (obj && obj->get_instance_id() == p_id) {
InspectorDock::get_inspector_singleton()->update_property(p_property);
}
}
void EditorDebuggerNode::_remote_object_requested(ObjectID p_id, int p_debugger) {
void EditorDebuggerNode::_remote_objects_requested(const TypedArray<uint64_t> &p_ids, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
inspect_edited_object_timeout = 0.7; // Temporarily disable timeout to avoid multiple requests.
get_current_debugger()->request_remote_object(p_id);
stop_waiting_inspection();
get_current_debugger()->request_remote_objects(p_ids);
}
void EditorDebuggerNode::_remote_selection_cleared(int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
stop_waiting_inspection();
get_current_debugger()->clear_inspector();
}
void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) {
@ -809,6 +870,17 @@ void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const No
});
}
void EditorDebuggerNode::set_debug_mute_audio(bool p_mute) {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
dbg->set_debug_mute_audio(p_mute);
});
debug_mute_audio = p_mute;
}
bool EditorDebuggerNode::get_debug_mute_audio() const {
return debug_mute_audio;
}
void EditorDebuggerNode::set_camera_override(CameraOverride p_override) {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
dbg->set_camera_override(p_override);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_NODE_H
#define EDITOR_DEBUGGER_NODE_H
#pragma once
#include "core/object/script_language.h"
#include "editor/debugger/editor_debugger_server.h"
@ -39,7 +38,7 @@ class Button;
class DebugAdapterParser;
class EditorDebuggerPlugin;
class EditorDebuggerTree;
class EditorDebuggerRemoteObject;
class EditorDebuggerRemoteObjects;
class MenuButton;
class ScriptEditorDebugger;
class TabContainer;
@ -103,21 +102,25 @@ private:
int last_error_count = 0;
int last_warning_count = 0;
bool inspect_edited_object_wait = false;
float inspect_edited_object_timeout = 0;
EditorDebuggerTree *remote_scene_tree = nullptr;
bool remote_scene_tree_wait = false;
float remote_scene_tree_timeout = 0.0;
bool remote_scene_tree_clear_msg = true;
bool auto_switch_remote_scene_tree = false;
bool debug_with_external_editor = false;
bool keep_open = false;
String current_uri;
bool debug_mute_audio = false;
CameraOverride camera_override = OVERRIDE_NONE;
HashMap<Breakpoint, bool, Breakpoint> breakpoints;
HashSet<Ref<EditorDebuggerPlugin>> debugger_plugins;
ScriptEditorDebugger *_add_debugger();
EditorDebuggerRemoteObject *get_inspected_remote_object();
void _update_errors();
friend class DebuggerEditorPlugin;
@ -129,12 +132,15 @@ protected:
void _debugger_stopped(int p_id);
void _debugger_wants_stop(int p_id);
void _debugger_changed(int p_tab);
void _remote_tree_select_requested(ObjectID p_id, int p_debugger);
void _debug_data(const String &p_msg, const Array &p_data, int p_debugger);
void _remote_tree_select_requested(const TypedArray<int64_t> &p_ids, int p_debugger);
void _remote_tree_clear_selection_requested(int p_debugger);
void _remote_tree_updated(int p_debugger);
void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _remote_object_updated(ObjectID p_id, int p_debugger);
void _remote_objects_updated(EditorDebuggerRemoteObjects *p_objs, int p_debugger);
void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger);
void _remote_object_requested(ObjectID p_id, int p_debugger);
void _remote_objects_requested(const TypedArray<uint64_t> &p_ids, int p_debugger);
void _remote_selection_cleared(int p_debugger);
void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger);
void _breakpoint_set_in_tree(Ref<RefCounted> p_script, int p_line, bool p_enabled, int p_debugger);
@ -184,6 +190,7 @@ public:
bool get_debug_with_external_editor() { return debug_with_external_editor; }
bool is_skip_breakpoints() const;
bool is_ignore_error_breaks() const;
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
void set_breakpoints(const String &p_path, const Array &p_lines);
void reload_all_scripts();
@ -191,6 +198,10 @@ public:
// Remote inspector/edit.
void request_remote_tree();
void set_remote_selection(const TypedArray<int64_t> &p_ids);
void clear_remote_tree_selection();
void stop_waiting_inspection();
bool match_remote_selection(const TypedArray<uint64_t> &p_ids) const;
static void _methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
static void _properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
@ -205,6 +216,9 @@ public:
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
void set_debug_mute_audio(bool p_mute);
bool get_debug_mute_audio() const;
void set_camera_override(CameraOverride p_override);
CameraOverride get_camera_override();
@ -218,5 +232,3 @@ public:
void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
};
#endif // EDITOR_DEBUGGER_NODE_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_SERVER_H
#define EDITOR_DEBUGGER_SERVER_H
#pragma once
#include "core/debugger/remote_debugger_peer.h"
#include "core/object/ref_counted.h"
@ -56,5 +55,3 @@ public:
virtual bool is_connection_available() const = 0;
virtual Ref<RemoteDebuggerPeer> take_connection() = 0;
};
#endif // EDITOR_DEBUGGER_SERVER_H

View file

@ -35,6 +35,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_toaster.h"
#include "editor/scene_tree_dock.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/gui/texture_rect.h"
@ -44,6 +45,7 @@
EditorDebuggerTree::EditorDebuggerTree() {
set_v_size_flags(SIZE_EXPAND_FILL);
set_allow_rmb_select(true);
set_select_mode(SELECT_MULTI);
// Popup
item_menu = memnew(PopupMenu);
@ -54,6 +56,9 @@ EditorDebuggerTree::EditorDebuggerTree() {
file_dialog = memnew(EditorFileDialog);
file_dialog->connect("file_selected", callable_mp(this, &EditorDebuggerTree::_file_selected));
add_child(file_dialog);
accept = memnew(AcceptDialog);
add_child(accept);
}
void EditorDebuggerTree::_notification(int p_what) {
@ -61,7 +66,8 @@ void EditorDebuggerTree::_notification(int p_what) {
case NOTIFICATION_POSTINITIALIZE: {
set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
connect("cell_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_selected));
connect("multi_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_selection_changed));
connect("nothing_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_nothing_selected));
connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded));
connect("item_mouse_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected));
} break;
@ -73,24 +79,57 @@ void EditorDebuggerTree::_notification(int p_what) {
}
void EditorDebuggerTree::_bind_methods() {
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("objects_selected", PropertyInfo(Variant::ARRAY, "object_ids"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("selection_cleared", PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("open"));
}
void EditorDebuggerTree::_scene_tree_selected() {
if (updating_scene_tree) {
void EditorDebuggerTree::_scene_tree_selection_changed(TreeItem *p_item, int p_column, bool p_selected) {
if (updating_scene_tree || !p_item) {
return;
}
TreeItem *item = get_selected();
if (!item) {
return;
uint64_t id = uint64_t(p_item->get_metadata(0));
if (p_selected) {
if (inspected_object_ids.size() == (int)EDITOR_GET("debugger/max_node_selection")) {
selection_surpassed_limit = true;
p_item->deselect(0);
return;
}
if (!inspected_object_ids.has(id)) {
inspected_object_ids.append(id);
}
} else if (inspected_object_ids.has(id)) {
inspected_object_ids.erase(id);
}
inspected_object_id = uint64_t(item->get_metadata(0));
if (!notify_selection_queued) {
callable_mp(this, &EditorDebuggerTree::_notify_selection_changed).call_deferred();
notify_selection_queued = true;
}
}
emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id);
void EditorDebuggerTree::_scene_tree_nothing_selected() {
deselect_all();
inspected_object_ids.clear();
emit_signal(SNAME("selection_cleared"), debugger_id);
}
void EditorDebuggerTree::_notify_selection_changed() {
notify_selection_queued = false;
if (inspected_object_ids.is_empty()) {
emit_signal(SNAME("selection_cleared"), debugger_id);
} else {
emit_signal(SNAME("objects_selected"), inspected_object_ids.duplicate(), debugger_id);
}
if (selection_surpassed_limit) {
selection_surpassed_limit = false;
EditorToaster::get_singleton()->popup_str(vformat(TTR("Some remote nodes were not selected, as the configured maximum selection is %d. This can be changed at \"debugger/max_node_selection\" in the Editor Settings."), EDITOR_GET("debugger/max_node_selection")), EditorToaster::SEVERITY_WARNING);
}
}
void EditorDebuggerTree::_scene_tree_folded(Object *p_obj) {
@ -124,7 +163,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
item->select(0);
item_menu->clear();
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CreateNewSceneFrom")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CreateNewSceneFrom")), TTR("Save Branch as Scene..."), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CopyNodePath")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("Collapse")), TTR("Expand/Collapse Branch"), ITEM_MENU_EXPAND_COLLAPSE);
item_menu->set_position(get_screen_position() + get_local_mouse_position());
@ -152,12 +191,13 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = true;
const String last_path = get_selected_path();
const String filter = SceneTreeDock::get_singleton()->get_filter();
TreeItem *select_item = nullptr;
LocalVector<TreeItem *> select_items;
bool hide_filtered_out_parents = EDITOR_GET("docks/scene_tree/hide_filtered_out_parents");
bool should_scroll = scrolling_to_item || filter != last_filter;
scrolling_to_item = false;
TreeItem *scroll_item = nullptr;
TypedArray<uint64_t> ids_present;
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
List<ParentItem> parents;
@ -216,9 +256,11 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
}
item->set_meta("node_path", current_path + "/" + item->get_text(0));
// Select previously selected node.
// Select previously selected nodes.
if (debugger_id == p_debugger) { // Can use remote id.
if (node.id == inspected_object_id) {
if (inspected_object_ids.has(uint64_t(node.id))) {
ids_present.append(node.id);
if (selection_uncollapse_all) {
selection_uncollapse_all = false;
@ -228,14 +270,14 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = true;
}
select_item = item;
select_items.push_back(item);
if (should_scroll) {
scroll_item = item;
}
}
} else if (last_path == (String)item->get_meta("node_path")) { // Must use path.
updating_scene_tree = false; // Force emission of new selection.
select_item = item;
updating_scene_tree = false; // Force emission of new selections.
select_items.push_back(item);
if (should_scroll) {
scroll_item = item;
}
@ -280,12 +322,12 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
break; // Filter matches, must survive.
}
parent->remove_child(item);
memdelete(item);
if (select_item == item || scroll_item == item) {
select_item = nullptr;
if (select_items.has(item) || scroll_item == item) {
select_items.resize(select_items.size() - 1);
scroll_item = nullptr;
}
parent->remove_child(item);
memdelete(item);
if (had_siblings) {
break; // Parent must survive.
@ -316,18 +358,20 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
from->get_parent()->remove_child(from);
memdelete(from);
if (select_item == from || scroll_item == from) {
select_item = nullptr;
if (select_items.has(from) || scroll_item == from) {
select_items.erase(from);
scroll_item = nullptr;
}
}
}
}
inspected_object_ids = ids_present;
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree.
if (select_item) {
select_item->select(0);
for (TreeItem *item : select_items) {
item->select(0);
}
if (scroll_item) {
scroll_to_item(scroll_item, false);
@ -337,12 +381,22 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = false;
}
void EditorDebuggerTree::select_node(ObjectID p_id) {
void EditorDebuggerTree::select_nodes(const TypedArray<int64_t> &p_ids) {
// Manually select, as the tree control may be out-of-date for some reason (e.g. not shown yet).
selection_uncollapse_all = true;
inspected_object_id = uint64_t(p_id);
inspected_object_ids = p_ids;
scrolling_to_item = true;
emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id);
if (!updating_scene_tree) {
// Request a tree refresh.
EditorDebuggerNode::get_singleton()->request_remote_tree();
}
// Set the value immediately, so no update flooding happens and causes a crash.
updating_scene_tree = true;
}
void EditorDebuggerTree::clear_selection() {
inspected_object_ids.clear();
if (!updating_scene_tree) {
// Request a tree refresh.
@ -453,8 +507,11 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
}
void EditorDebuggerTree::_file_selected(const String &p_file) {
if (inspected_object_id.is_null()) {
if (inspected_object_ids.size() != 1) {
accept->set_text(vformat(TTR("Saving the branch as a scene requires selecting only one node, but you have selected %d nodes."), inspected_object_ids.size()));
accept->popup_centered();
return;
}
emit_signal(SNAME("save_node"), inspected_object_id, p_file, debugger_id);
emit_signal(SNAME("save_node"), inspected_object_ids[0], p_file, debugger_id);
}

View file

@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_TREE_H
#define EDITOR_DEBUGGER_TREE_H
#pragma once
#include "scene/gui/tree.h"
class AcceptDialog;
class SceneDebuggerTree;
class EditorFileDialog;
@ -58,18 +58,23 @@ private:
ITEM_MENU_EXPAND_COLLAPSE,
};
ObjectID inspected_object_id;
TypedArray<uint64_t> inspected_object_ids;
int debugger_id = 0;
bool updating_scene_tree = false;
bool scrolling_to_item = false;
bool notify_selection_queued = false;
bool selection_surpassed_limit = false;
bool selection_uncollapse_all = false;
HashSet<ObjectID> unfold_cache;
PopupMenu *item_menu = nullptr;
EditorFileDialog *file_dialog = nullptr;
AcceptDialog *accept = nullptr;
String last_filter;
void _scene_tree_folded(Object *p_obj);
void _scene_tree_selected();
void _scene_tree_selection_changed(TreeItem *p_item, int p_column, bool p_selected);
void _scene_tree_nothing_selected();
void _notify_selection_changed();
void _scene_tree_rmb_selected(const Vector2 &p_position, MouseButton p_button);
void _item_menu_id_pressed(int p_option);
void _file_selected(const String &p_file);
@ -90,9 +95,10 @@ public:
String get_selected_path();
ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger.
inline TypedArray<uint64_t> get_selection() const { return inspected_object_ids.duplicate(); }
void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger);
void select_node(ObjectID p_id);
void select_nodes(const TypedArray<int64_t> &p_ids);
void clear_selection();
EditorDebuggerTree();
};
#endif // EDITOR_DEBUGGER_TREE_H

View file

@ -74,7 +74,8 @@ void EditorExpressionEvaluator::_clear() {
}
void EditorExpressionEvaluator::_remote_object_selected(ObjectID p_id) {
editor_debugger->emit_signal(SNAME("remote_object_requested"), p_id);
Array arr = { p_id };
editor_debugger->emit_signal(SNAME("remote_objects_requested"), arr);
}
void EditorExpressionEvaluator::_on_expression_input_changed(const String &p_expression) {
@ -109,6 +110,7 @@ EditorExpressionEvaluator::EditorExpressionEvaluator() {
expression_input = memnew(LineEdit);
expression_input->set_h_size_flags(Control::SIZE_EXPAND_FILL);
expression_input->set_placeholder(TTR("Expression to evaluate"));
expression_input->set_accessibility_name(TTRC("Expression"));
expression_input->set_clear_button_enabled(true);
expression_input->connect(SceneStringName(text_submitted), callable_mp(this, &EditorExpressionEvaluator::_evaluate).unbind(1));
expression_input->connect(SceneStringName(text_changed), callable_mp(this, &EditorExpressionEvaluator::_on_expression_input_changed));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_EXPRESSION_EVALUATOR_H
#define EDITOR_EXPRESSION_EVALUATOR_H
#pragma once
#include "scene/gui/box_container.h"
@ -73,5 +72,3 @@ public:
EditorExpressionEvaluator();
};
#endif // EDITOR_EXPRESSION_EVALUATOR_H

View file

@ -78,7 +78,7 @@ void EditorFileServer::_scan_files_changed(EditorFileSystemDirectory *efd, const
uint64_t mt = FileAccess::get_modified_time(remapped_path);
_add_file(remapped_path, mt, files_to_send, cached_files);
} else if (remap.begins_with("path.")) {
String feature = remap.get_slice(".", 1);
String feature = remap.get_slicec('.', 1);
if (p_tags.has(feature)) {
String remapped_path = cf->get_value("remap", remap);
uint64_t mt = FileAccess::get_modified_time(remapped_path);
@ -200,7 +200,7 @@ void EditorFileServer::poll() {
// Scan files to send.
_scan_files_changed(EditorFileSystem::get_singleton()->get_filesystem(), tags, files_to_send, cached_files);
// Add forced export files
Vector<String> forced_export = EditorExportPlatform::get_forced_export_files();
Vector<String> forced_export = EditorExportPlatform::get_forced_export_files(Ref<EditorExportPreset>());
for (int i = 0; i < forced_export.size(); i++) {
_add_custom_file(forced_export[i], files_to_send, cached_files);
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_FILE_SERVER_H
#define EDITOR_FILE_SERVER_H
#pragma once
#include "core/io/tcp_server.h"
#include "core/os/thread.h"
@ -55,5 +54,3 @@ public:
EditorFileServer();
~EditorFileServer();
};
#endif // EDITOR_FILE_SERVER_H

View file

@ -37,8 +37,6 @@
#include "editor/themes/editor_theme_manager.h"
#include "main/performance.h"
EditorPerformanceProfiler::Monitor::Monitor() {}
EditorPerformanceProfiler::Monitor::Monitor(const String &p_name, const String &p_base, int p_frame_index, Performance::MonitorType p_type, TreeItem *p_item) {
type = p_type;
item = p_item;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PERFORMANCE_PROFILER_H
#define EDITOR_PERFORMANCE_PROFILER_H
#pragma once
#include "core/templates/hash_map.h"
#include "main/performance.h"
@ -52,7 +51,7 @@ private:
Performance::MonitorType type = Performance::MONITOR_TYPE_QUANTITY;
int frame_index = 0;
Monitor();
Monitor() {}
Monitor(const String &p_name, const String &p_base, int p_frame_index, Performance::MonitorType p_type, TreeItem *p_item);
void update_value(float p_value);
void reset();
@ -88,5 +87,3 @@ public:
List<float> *get_monitor_data(const StringName &p_name);
EditorPerformanceProfiler();
};
#endif // EDITOR_PERFORMANCE_PROFILER_H

View file

@ -146,7 +146,7 @@ String EditorProfiler::_get_time_as_text(const Metric &m, float p_time, int p_ca
Color EditorProfiler::_get_color_from_signature(const StringName &p_signature) const {
Color bc = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
double rot = ABS(double(p_signature.hash()) / double(0x7FFFFFFF));
double rot = Math::abs(double(p_signature.hash()) / double(0x7FFFFFFF));
Color c;
c.set_hsv(rot, bc.get_s(), bc.get_v());
return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);
@ -694,6 +694,7 @@ EditorProfiler::EditorProfiler() {
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
display_mode->set_accessibility_name(TTRC("Measure"));
display_mode->add_item(TTR("Frame Time (ms)"));
display_mode->add_item(TTR("Average Time (ms)"));
display_mode->add_item(TTR("Frame %"));
@ -709,6 +710,7 @@ EditorProfiler::EditorProfiler() {
hb_time->add_child(memnew(Label(TTR("Time:"))));
display_time = memnew(OptionButton);
display_time->set_accessibility_name(TTRC("Time"));
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, including the time spent in other functions called by that function.
display_time->add_item(TTR("Inclusive"));
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, exincluding the time spent in other functions called by that function.
@ -731,6 +733,7 @@ EditorProfiler::EditorProfiler() {
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
cursor_metric_edit = memnew(SpinBox);
cursor_metric_edit->set_accessibility_name(TTRC("Frame"));
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
cursor_metric_edit->set_value(0);
cursor_metric_edit->set_editable(false);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PROFILER_H
#define EDITOR_PROFILER_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -186,5 +185,3 @@ public:
EditorProfiler();
};
#endif // EDITOR_PROFILER_H

View file

@ -136,7 +136,7 @@ String EditorVisualProfiler::_get_time_as_text(float p_time) {
Color EditorVisualProfiler::_get_color_from_signature(const StringName &p_signature) const {
Color bc = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
double rot = ABS(double(p_signature.hash()) / double(0x7FFFFFFF));
double rot = Math::abs(double(p_signature.hash()) / double(0x7FFFFFFF));
Color c;
c.set_hsv(rot, bc.get_s(), bc.get_v());
return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);
@ -362,7 +362,7 @@ void EditorVisualProfiler::_update_frame(bool p_focus_selected) {
stack.push_back(category);
categories.push_back(category);
name = name.substr(1, name.length());
name = name.substr(1);
category->set_text(0, name);
category->set_metadata(1, cpu_time);
@ -778,6 +778,7 @@ EditorVisualProfiler::EditorVisualProfiler() {
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
display_mode->set_accessibility_name(TTRC("Measure"));
display_mode->add_item(TTR("Frame Time (ms)"));
display_mode->add_item(TTR("Frame %"));
display_mode->connect(SceneStringName(item_selected), callable_mp(this, &EditorVisualProfiler::_combo_changed));
@ -801,6 +802,7 @@ EditorVisualProfiler::EditorVisualProfiler() {
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
cursor_metric_edit = memnew(SpinBox);
cursor_metric_edit->set_accessibility_name(TTRC("Frame"));
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
hb_frame->add_child(cursor_metric_edit);
cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorVisualProfiler::_cursor_metric_changed));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_VISUAL_PROFILER_H
#define EDITOR_VISUAL_PROFILER_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -153,5 +152,3 @@ public:
EditorVisualProfiler();
};
#endif // EDITOR_VISUAL_PROFILER_H

File diff suppressed because it is too large Load diff

View file

@ -28,17 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCRIPT_EDITOR_DEBUGGER_H
#define SCRIPT_EDITOR_DEBUGGER_H
#pragma once
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "editor/debugger/editor_debugger_inspector.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/editor_debugger_server.h"
#include "scene/gui/button.h"
#include "scene/gui/margin_container.h"
class Button;
class Tree;
class LineEdit;
class TabContainer;
@ -115,6 +113,7 @@ private:
int warning_count;
bool skip_breakpoints_value = false;
bool ignore_error_breaks_value = false;
Ref<Script> stack_script;
TabContainer *tabs = nullptr;
@ -122,6 +121,7 @@ private:
Label *reason = nullptr;
Button *skip_breakpoints = nullptr;
Button *ignore_error_breaks = nullptr;
Button *copy = nullptr;
Button *step = nullptr;
Button *next = nullptr;
@ -147,7 +147,7 @@ private:
Ref<RemoteDebuggerPeer> peer;
HashMap<NodePath, int> node_path_cache;
int last_path_id;
int last_path_id = 0;
HashMap<String, int> res_path_cache;
EditorProfiler *profiler = nullptr;
@ -159,7 +159,7 @@ private:
bool move_to_foreground = true;
bool can_request_idle_draw = false;
bool live_debug;
bool live_debug = true;
uint64_t debugging_thread_id = Thread::UNASSIGNED_ID;
@ -183,16 +183,54 @@ private:
void _select_thread(int p_index);
bool debug_mute_audio = false;
EditorDebuggerNode::CameraOverride camera_override;
void _stack_dump_frame_selected();
void _file_selected(const String &p_file);
/// Message handler function for _parse_message.
typedef void (ScriptEditorDebugger::*ParseMessageFunc)(uint64_t p_thread_id, const Array &p_data);
static HashMap<String, ParseMessageFunc> parse_message_handlers;
static void _init_parse_message_handlers();
void _msg_debug_enter(uint64_t p_thread_id, const Array &p_data);
void _msg_debug_exit(uint64_t p_thread_id, const Array &p_data);
void _msg_set_pid(uint64_t p_thread_id, const Array &p_data);
void _msg_scene_click_ctrl(uint64_t p_thread_id, const Array &p_data);
void _msg_scene_scene_tree(uint64_t p_thread_id, const Array &p_data);
void _msg_scene_inspect_objects(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_memory_usage(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_drawn(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_dump(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_frame_vars(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_frame_var(uint64_t p_thread_id, const Array &p_data);
void _msg_output(uint64_t p_thread_id, const Array &p_data);
void _msg_performance_profile_frame(uint64_t p_thread_id, const Array &p_data);
void _msg_visual_hardware_info(uint64_t p_thread_id, const Array &p_data);
void _msg_visual_profile_frame(uint64_t p_thread_id, const Array &p_data);
void _msg_error(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_function_signature(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_profile_common(const Array &p_data, const bool p_final);
void _msg_servers_profile_frame(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_profile_total(uint64_t p_thread_id, const Array &p_data);
void _msg_request_quit(uint64_t p_thread_id, const Array &p_data);
void _msg_remote_objects_selected(uint64_t p_thread_id, const Array &p_data);
void _msg_remote_nothing_selected(uint64_t p_thread_id, const Array &p_data);
void _msg_remote_selection_invalidated(uint64_t p_thread_id, const Array &p_data);
void _msg_show_selection_limit_warning(uint64_t p_thread_id, const Array &p_data);
void _msg_performance_profile_names(uint64_t p_thread_id, const Array &p_data);
void _msg_filesystem_update_file(uint64_t p_thread_id, const Array &p_data);
void _msg_evaluation_return(uint64_t p_thread_id, const Array &p_data);
void _msg_window_title(uint64_t p_thread_id, const Array &p_data);
void _parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data);
void _set_reason_text(const String &p_reason, MessageType p_type);
void _update_buttons_state();
void _remote_object_selected(ObjectID p_object);
void _remote_object_edited(ObjectID, const String &p_prop, const Variant &p_value);
void _remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field);
void _remote_object_property_updated(ObjectID p_id, const String &p_property);
void _video_mem_request();
@ -216,6 +254,8 @@ private:
void _expand_errors_list();
void _collapse_errors_list();
void _vmem_item_activated();
void _profiler_activate(bool p_enable, int p_profiler);
void _profiler_seeked();
@ -246,9 +286,10 @@ protected:
static void _bind_methods();
public:
void request_remote_object(ObjectID p_obj_id);
void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value);
Object *get_remote_object(ObjectID p_id);
void request_remote_objects(const TypedArray<uint64_t> &p_obj_ids, bool p_update_selection = true);
void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value, const String &p_field = "");
void clear_inspector(bool p_send_msg = true);
// Needed by _live_edit_set, buttons state.
void set_editor_remote_tree(const Tree *p_tree) { editor_remote_tree = p_tree; }
@ -262,6 +303,7 @@ public:
void stop();
void debug_skip_breakpoints();
void debug_ignore_error_breaks();
void debug_copy();
void debug_next();
@ -299,6 +341,9 @@ public:
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
bool get_debug_mute_audio() const;
void set_debug_mute_audio(bool p_mute);
EditorDebuggerNode::CameraOverride get_camera_override() const;
void set_camera_override(EditorDebuggerNode::CameraOverride p_override);
@ -309,7 +354,8 @@ public:
void reload_all_scripts();
void reload_scripts(const Vector<String> &p_script_paths);
bool is_skip_breakpoints();
bool is_skip_breakpoints() const;
bool is_ignore_error_breaks() const;
virtual Size2 get_minimum_size() const override;
@ -324,5 +370,3 @@ public:
ScriptEditorDebugger();
~ScriptEditorDebugger();
};
#endif // SCRIPT_EDITOR_DEBUGGER_H

View file

@ -317,12 +317,8 @@ void DependencyEditorOwners::_list_rmb_clicked(int p_item, const Vector2 &p_pos,
void DependencyEditorOwners::_select_file(int p_idx) {
String fpath = owners->get_item_text(p_idx);
EditorNode::get_singleton()->load_scene_or_resource(fpath);
if (ResourceLoader::get_resource_type(fpath) == "PackedScene") {
EditorNode::get_singleton()->open_request(fpath);
} else {
EditorNode::get_singleton()->load_resource(fpath);
}
hide();
emit_signal(SceneStringName(confirmed));
}
@ -467,17 +463,16 @@ void DependencyRemoveDialog::_find_localization_remaps_of_removed_files(Vector<R
p_removed.push_back(dep);
}
Array remap_keys = remaps.keys();
for (int j = 0; j < remap_keys.size(); j++) {
PackedStringArray remapped_files = remaps[remap_keys[j]];
for (int k = 0; k < remapped_files.size(); k++) {
int splitter_pos = remapped_files[k].rfind_char(':');
String res_path = remapped_files[k].substr(0, splitter_pos);
for (const KeyValue<Variant, Variant> &remap_kv : remaps) {
PackedStringArray remapped_files = remap_kv.value;
for (const String &remapped_file : remapped_files) {
int splitter_pos = remapped_file.rfind_char(':');
String res_path = remapped_file.substr(0, splitter_pos);
if (res_path == path) {
String locale_name = remapped_files[k].substr(splitter_pos + 1);
String locale_name = remapped_file.substr(splitter_pos + 1);
RemovedDependency dep;
dep.file = vformat(TTR("Localization remap for path '%s' and locale '%s'."), remap_keys[j], locale_name);
dep.file = vformat(TTR("Localization remap for path '%s' and locale '%s'."), remap_kv.key, locale_name);
dep.file_type = "";
dep.dependency = path;
dep.dependency_folder = files.value;
@ -589,34 +584,19 @@ void DependencyRemoveDialog::ok_pressed() {
}
}
HashMap<String, StringName> setting_path_map;
for (const StringName &setting : path_project_settings) {
const String path = ResourceUID::ensure_path(GLOBAL_GET(setting));
setting_path_map[path] = setting;
}
bool project_settings_modified = false;
for (const String &file : files_to_delete) {
// If the file we are deleting for e.g. the main scene, default environment,
// or audio bus layout, we must clear its definition in Project Settings.
if (file == ResourceUID::ensure_path(GLOBAL_GET("application/config/icon"))) {
ProjectSettings::get_singleton()->set("application/config/icon", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("application/run/main_scene"))) {
ProjectSettings::get_singleton()->set("application/run/main_scene", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("application/boot_splash/image"))) {
ProjectSettings::get_singleton()->set("application/boot_splash/image", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("gui/theme/custom"))) {
ProjectSettings::get_singleton()->set("gui/theme/custom", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("gui/theme/custom_font"))) {
ProjectSettings::get_singleton()->set("gui/theme/custom_font", "");
project_settings_modified = true;
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("audio/buses/default_bus_layout"))) {
ProjectSettings::get_singleton()->set("audio/buses/default_bus_layout", "");
project_settings_modified = true;
const StringName *setting_name = setting_path_map.getptr(file);
if (setting_name) {
ProjectSettings::get_singleton()->set(*setting_name, "");
}
const String path = OS::get_singleton()->get_resource_dir() + file.replace_first("res://", "/");
@ -632,7 +612,7 @@ void DependencyRemoveDialog::ok_pressed() {
ProjectSettings::get_singleton()->save();
}
if (dirs_to_delete.size() == 0) {
if (dirs_to_delete.is_empty()) {
// If we only deleted files we should only need to tell the file system about the files we touched.
for (int i = 0; i < files_to_delete.size(); ++i) {
EditorFileSystem::get_singleton()->update_file(files_to_delete[i]);
@ -698,6 +678,7 @@ DependencyRemoveDialog::DependencyRemoveDialog() {
files_to_delete_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
files_to_delete_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
files_to_delete_list->set_custom_minimum_size(Size2(0, 94) * EDSCALE);
files_to_delete_list->set_accessibility_name(TTRC("Files to be Deleted"));
vb->add_child(files_to_delete_list);
vb_owners = memnew(VBoxContainer);
@ -714,14 +695,22 @@ DependencyRemoveDialog::DependencyRemoveDialog() {
owners->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
owners->set_hide_root(true);
owners->set_custom_minimum_size(Size2(0, 94) * EDSCALE);
owners->set_accessibility_name(TTRC("Dependencies"));
vb_owners->add_child(owners);
owners->set_v_size_flags(Control::SIZE_EXPAND_FILL);
List<PropertyInfo> property_list;
ProjectSettings::get_singleton()->get_property_list(&property_list);
for (const PropertyInfo &pi : property_list) {
if (pi.type == Variant::STRING && pi.hint == PROPERTY_HINT_FILE) {
path_project_settings.push_back(pi.name);
}
}
}
//////////////
void DependencyErrorDialog::show(Mode p_mode, const String &p_for_file, const Vector<String> &report) {
mode = p_mode;
void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> &report) {
for_file = p_for_file;
set_title(TTR("Error loading:") + " " + p_for_file.get_file());
files->clear();
@ -746,14 +735,7 @@ void DependencyErrorDialog::show(Mode p_mode, const String &p_for_file, const Ve
}
void DependencyErrorDialog::ok_pressed() {
switch (mode) {
case MODE_SCENE:
EditorNode::get_singleton()->load_scene(for_file, true);
break;
case MODE_RESOURCE:
EditorNode::get_singleton()->load_resource(for_file, true);
break;
}
EditorNode::get_singleton()->load_scene_or_resource(for_file, true);
}
void DependencyErrorDialog::custom_action(const String &) {
@ -778,8 +760,6 @@ DependencyErrorDialog::DependencyErrorDialog() {
vb->add_child(text);
text->set_text(TTR("Which action should be taken?"));
mode = Mode::MODE_RESOURCE;
fdep = add_button(TTR("Fix Dependencies"), true, "fixdeps");
set_title(TTR("Errors loading!"));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEPENDENCY_EDITOR_H
#define DEPENDENCY_EDITOR_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/dialogs.h"
@ -120,6 +119,8 @@ class DependencyRemoveDialog : public ConfirmationDialog {
}
};
LocalVector<StringName> path_project_settings;
void _find_files_in_removed_folder(EditorFileSystemDirectory *efsd, const String &p_folder);
void _find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed);
void _find_localization_remaps_of_removed_files(Vector<RemovedDependency> &p_removed);
@ -138,12 +139,6 @@ public:
class DependencyErrorDialog : public ConfirmationDialog {
GDCLASS(DependencyErrorDialog, ConfirmationDialog);
public:
enum Mode {
MODE_SCENE,
MODE_RESOURCE,
};
private:
String for_file;
Mode mode;
@ -154,7 +149,7 @@ private:
void custom_action(const String &) override;
public:
void show(Mode p_mode, const String &p_for_file, const Vector<String> &report);
void show(const String &p_for_file, const Vector<String> &report);
DependencyErrorDialog();
};
@ -179,5 +174,3 @@ public:
void show();
OrphanResourcesDialog();
};
#endif // DEPENDENCY_EDITOR_H

View file

@ -166,6 +166,7 @@ DirectoryCreateDialog::DirectoryCreateDialog() {
vb->add_child(name_label);
dir_path = memnew(LineEdit);
dir_path->set_accessibility_name(TTRC("Name"));
vb->add_child(dir_path);
register_text_enter(dir_path);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DIRECTORY_CREATE_DIALOG_H
#define DIRECTORY_CREATE_DIALOG_H
#pragma once
#include "scene/gui/dialogs.h"
@ -68,5 +67,3 @@ public:
DirectoryCreateDialog();
};
#endif // DIRECTORY_CREATE_DIALOG_H

View file

@ -368,6 +368,15 @@ void DocTools::remove_doc(const String &p_class_name) {
class_list.erase(p_class_name);
}
void DocTools::remove_script_doc_by_path(const String &p_path) {
for (KeyValue<String, DocData::ClassDoc> &E : class_list) {
if (E.value.is_script_doc && E.value.script_path == p_path) {
remove_doc(E.key);
return;
}
}
}
bool DocTools::has_doc(const String &p_class_name) {
if (p_class_name.is_empty()) {
return false;
@ -385,9 +394,9 @@ static Variant get_documentation_default_value(const StringName &p_class_name, c
// Cannot get default value of classes that can't be instantiated
List<StringName> inheriting_classes;
ClassDB::get_direct_inheriters_from_class(p_class_name, &inheriting_classes);
for (List<StringName>::Element *E2 = inheriting_classes.front(); E2; E2 = E2->next()) {
if (ClassDB::can_instantiate(E2->get())) {
default_value = ClassDB::class_get_default_property_value(E2->get(), p_property_name, &r_default_value_valid);
for (const StringName &class_name : inheriting_classes) {
if (ClassDB::can_instantiate(class_name)) {
default_value = ClassDB::class_get_default_property_value(class_name, p_property_name, &r_default_value_valid);
if (r_default_value_valid) {
break;
}
@ -619,7 +628,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
// Don't skip parametric setters and getters, i.e. method which require
// one or more parameters to define what property should be set or retrieved.
// E.g. CPUParticles3D::set_param(Parameter param, float value).
if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) {
if (E.arguments.is_empty() /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) {
continue;
}
}
@ -648,11 +657,10 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
ClassDB::get_signal_list(name, &signal_list, true);
if (signal_list.size()) {
for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) {
for (const MethodInfo &mi : signal_list) {
DocData::MethodDoc signal;
signal.name = EV->get().name;
for (List<PropertyInfo>::Element *EA = EV->get().arguments.front(); EA; EA = EA->next()) {
const PropertyInfo &arginfo = EA->get();
signal.name = mi.name;
for (const PropertyInfo &arginfo : mi.arguments) {
DocData::ArgumentDoc argument;
DocData::argument_doc_from_arginfo(argument, arginfo);
@ -844,9 +852,8 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
method.name = mi.name;
int j = 0;
for (List<PropertyInfo>::ConstIterator itr = mi.arguments.begin(); itr != mi.arguments.end(); ++itr, ++j) {
PropertyInfo arginfo = *itr;
for (int64_t j = 0; j < mi.arguments.size(); ++j) {
const PropertyInfo &arginfo = mi.arguments[j];
DocData::ArgumentDoc ad;
DocData::argument_doc_from_arginfo(ad, arginfo);
ad.name = arginfo.name;
@ -933,7 +940,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
DocData::ConstantDoc constant;
constant.name = E;
Variant value = Variant::get_constant_value(Variant::Type(i), E);
constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string().replace("\n", " ");
constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string().replace_char('\n', ' ');
constant.is_value_valid = true;
constant.type = Variant::get_type_name(value.get_type());
c.constants.push_back(constant);
@ -1057,10 +1064,9 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
DocData::return_doc_from_retinfo(md, mi.return_val);
int j = 0;
for (List<PropertyInfo>::ConstIterator itr = mi.arguments.begin(); itr != mi.arguments.end(); ++itr, ++j) {
for (int64_t j = 0; j < mi.arguments.size(); ++j) {
DocData::ArgumentDoc ad;
DocData::argument_doc_from_arginfo(ad, *itr);
DocData::argument_doc_from_arginfo(ad, mi.arguments[j]);
int darg_idx = j - (mi.arguments.size() - mi.default_arguments.size());
if (darg_idx >= 0) {
@ -1103,12 +1109,11 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
DocData::return_doc_from_retinfo(atd, ai.return_val);
int j = 0;
for (List<PropertyInfo>::ConstIterator itr = ai.arguments.begin(); itr != ai.arguments.end(); ++itr, ++j) {
for (int64_t j = 0; j < ai.arguments.size(); ++j) {
DocData::ArgumentDoc ad;
DocData::argument_doc_from_arginfo(ad, *itr);
DocData::argument_doc_from_arginfo(ad, ai.arguments[j]);
int darg_idx = j - (ai.arguments.size() - ai.default_arguments.size());
int64_t darg_idx = j - (ai.arguments.size() - ai.default_arguments.size());
if (darg_idx >= 0) {
ad.default_value = DocData::get_default_value_string(ai.default_arguments[darg_idx]);
}
@ -1632,7 +1637,7 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
}
Error err;
String save_file = save_path.path_join(c.name.replace("\"", "").replace("/", "--") + ".xml");
String save_file = save_path.path_join(c.name.remove_char('\"').replace("/", "--") + ".xml");
Ref<FileAccess> f = FileAccess::open(save_file, FileAccess::WRITE, &err);
ERR_CONTINUE_MSG(err != OK, "Can't write doc file: " + save_file + ".");

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DOC_TOOLS_H
#define DOC_TOOLS_H
#pragma once
#include "core/doc_data.h"
#include "core/templates/rb_set.h"
@ -45,6 +44,7 @@ public:
void merge_from(const DocTools &p_data);
void add_doc(const DocData::ClassDoc &p_class_doc);
void remove_doc(const String &p_class_name);
void remove_script_doc_by_path(const String &p_path);
bool has_doc(const String &p_class_name);
enum GenerateFlags {
GENERATE_FLAG_SKIP_BASIC_TYPES = (1 << 0),
@ -58,5 +58,3 @@ public:
Error load_compressed(const uint8_t *p_data, int p_compressed_size, int p_uncompressed_size);
Error load_xml(const uint8_t *p_data, int p_size);
};
#endif // DOC_TOOLS_H

View file

@ -133,8 +133,8 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St
while (*names_ptr) {
const String name = String::utf8(*names_ptr++);
const String identifier = name.get_slice("<", 0);
const String website = name.get_slice_count("<") == 1 ? "" : name.get_slice("<", 1).trim_suffix(">");
const String identifier = name.get_slicec('<', 0);
const String website = name.get_slice_count("<") == 1 ? "" : name.get_slicec('<', 1).trim_suffix(">");
const int name_item_id = il->add_item(identifier, nullptr, false);
il->set_item_tooltip_enabled(name_item_id, false);
@ -340,5 +340,3 @@ EditorAbout::EditorAbout() {
tpl_ti_all->select(0);
_tpl_text->set_text(tpl_ti_all->get_metadata(0));
}
EditorAbout::~EditorAbout() {}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_ABOUT_H
#define EDITOR_ABOUT_H
#pragma once
#include "scene/gui/dialogs.h"
#include "scene/gui/item_list.h"
@ -62,7 +61,4 @@ protected:
public:
EditorAbout();
~EditorAbout();
};
#endif // EDITOR_ABOUT_H

View file

@ -684,6 +684,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
show_source_files_button = memnew(Button);
show_source_files_button->set_toggle_mode(true);
show_source_files_button->set_tooltip_text(TTR("Open the list of the asset contents and select which files to install."));
show_source_files_button->set_accessibility_name(TTRC("Show Asset Contents"));
remapping_tools->add_child(show_source_files_button);
show_source_files_button->connect(SceneStringName(toggled), callable_mp(this, &EditorAssetInstaller::_toggle_source_tree).bind(false));
@ -708,6 +709,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
asset_conflicts_label->set_text(TTR("No files conflict with your project"));
remapping_tools->add_child(asset_conflicts_label);
asset_conflicts_link = memnew(LinkButton);
asset_conflicts_link->set_accessibility_name(TTRC("Show Conflicting Files"));
asset_conflicts_link->set_theme_type_variation("HeaderSmallLink");
asset_conflicts_link->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
asset_conflicts_link->set_tooltip_text(TTR("Show contents of the asset and conflicting files."));
@ -732,6 +734,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
source_tree_vb->add_child(source_tree_label);
source_tree = memnew(Tree);
source_tree->set_accessibility_name(TTRC("Source Files"));
source_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
source_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
source_tree->connect("item_edited", callable_mp(this, &EditorAssetInstaller::_item_checked_cbk));
@ -748,6 +751,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
destination_tree_vb->add_child(destination_tree_label);
destination_tree = memnew(Tree);
destination_tree->set_accessibility_name(TTRC("Destination Files"));
destination_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
destination_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
destination_tree->connect("item_edited", callable_mp(this, &EditorAssetInstaller::_item_checked_cbk));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_ASSET_INSTALLER_H
#define EDITOR_ASSET_INSTALLER_H
#pragma once
#include "scene/gui/dialogs.h"
#include "scene/gui/tree.h"
@ -105,5 +104,3 @@ public:
EditorAssetInstaller();
};
#endif // EDITOR_ASSET_INSTALLER_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_ATLAS_PACKER_H
#define EDITOR_ATLAS_PACKER_H
#pragma once
#include "core/math/vector2.h"
#include "core/math/vector2i.h"
@ -68,5 +67,3 @@ private:
public:
static void chart_pack(Vector<Chart> &charts, int &r_width, int &r_height, int p_atlas_max_size = 2048, int p_cell_resolution = 4);
};
#endif // EDITOR_ATLAS_PACKER_H

View file

@ -130,6 +130,14 @@ void EditorAudioBus::_notification(int p_what) {
set_process(true);
} break;
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Audio bus editor")));
} break;
case NOTIFICATION_DRAW: {
if (is_master) {
draw_style_box(get_theme_stylebox(SNAME("disabled"), SNAME("Button")), Rect2(Vector2(), get_size()));
@ -583,6 +591,15 @@ void EditorAudioBus::gui_input(const Ref<InputEvent> &p_event) {
bus_popup->reset_size();
bus_popup->popup();
}
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed() && k->is_action("ui_menu", true)) {
bus_popup->set_position(get_screen_position());
bus_popup->reset_size();
bus_popup->popup();
accept_event();
}
}
void EditorAudioBus::_effects_gui_input(Ref<InputEvent> p_event) {
@ -619,7 +636,7 @@ Variant EditorAudioBus::get_drag_data(const Point2 &p_point) {
p->set_modulate(Color(1, 1, 1, 0.7));
p->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("focus"), SNAME("Button")));
p->set_size(get_size());
p->set_position(-p_point);
p->set_position((p_point == Vector2(Math::INF, Math::INF)) ? Vector2() : -p_point);
set_drag_preview(c);
Dictionary d;
d["type"] = "move_audio_bus";
@ -652,7 +669,7 @@ void EditorAudioBus::drop_data(const Point2 &p_point, const Variant &p_data) {
}
Variant EditorAudioBus::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
TreeItem *item = effects->get_item_at_position(p_point);
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? effects->get_selected() : effects->get_item_at_position(p_point);
if (!item) {
return Variant();
}
@ -681,7 +698,7 @@ bool EditorAudioBus::can_drop_data_fw(const Point2 &p_point, const Variant &p_da
return false;
}
TreeItem *item = effects->get_item_at_position(p_point);
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? effects->get_selected() : effects->get_item_at_position(p_point);
if (!item) {
return false;
}
@ -694,11 +711,11 @@ bool EditorAudioBus::can_drop_data_fw(const Point2 &p_point, const Variant &p_da
void EditorAudioBus::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
Dictionary d = p_data;
TreeItem *item = effects->get_item_at_position(p_point);
TreeItem *item = (p_point == Vector2(Math::INF, Math::INF)) ? effects->get_selected() : effects->get_item_at_position(p_point);
if (!item) {
return;
}
int pos = effects->get_drop_section_at_position(p_point);
int pos = (p_point == Vector2(Math::INF, Math::INF)) ? effects->get_drop_section_at_position(effects->get_item_rect(item).position) : effects->get_drop_section_at_position(p_point);
Variant md = item->get_metadata(0);
int paste_at;
@ -815,6 +832,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
set_v_size_flags(SIZE_EXPAND_FILL);
track_name = memnew(LineEdit);
track_name->set_accessibility_name(TTRC("Track Name"));
track_name->connect(SceneStringName(text_submitted), callable_mp(this, &EditorAudioBus::_name_changed));
track_name->connect(SceneStringName(focus_exited), callable_mp(this, &EditorAudioBus::_name_focus_exit));
vb->add_child(track_name);
@ -825,6 +843,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
solo->set_theme_type_variation(SceneStringName(FlatButton));
solo->set_toggle_mode(true);
solo->set_tooltip_text(TTR("Solo"));
solo->set_accessibility_name(TTRC("Solo"));
solo->set_focus_mode(FOCUS_NONE);
solo->connect(SceneStringName(pressed), callable_mp(this, &EditorAudioBus::_solo_toggled));
hbc->add_child(solo);
@ -832,6 +851,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
mute->set_theme_type_variation(SceneStringName(FlatButton));
mute->set_toggle_mode(true);
mute->set_tooltip_text(TTR("Mute"));
mute->set_accessibility_name(TTRC("Mute"));
mute->set_focus_mode(FOCUS_NONE);
mute->connect(SceneStringName(pressed), callable_mp(this, &EditorAudioBus::_mute_toggled));
hbc->add_child(mute);
@ -839,6 +859,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
bypass->set_theme_type_variation(SceneStringName(FlatButton));
bypass->set_toggle_mode(true);
bypass->set_tooltip_text(TTR("Bypass"));
bypass->set_accessibility_name(TTRC("Bypass"));
bypass->set_focus_mode(FOCUS_NONE);
bypass->connect(SceneStringName(pressed), callable_mp(this, &EditorAudioBus::_bypass_toggled));
hbc->add_child(bypass);
@ -886,6 +907,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
slider->set_max(1.0);
slider->set_step(0.0001);
slider->set_clip_contents(false);
slider->set_accessibility_name(TTRC("Volume"));
audio_value_preview_box = memnew(Panel);
slider->add_child(audio_value_preview_box);
@ -922,6 +944,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
channel[i].vu_l->set_min(-80);
channel[i].vu_l->set_max(24);
channel[i].vu_l->set_step(0.1);
channel[i].vu_l->set_accessibility_name(vformat(TTR("Channel %d, Left VU"), i));
channel[i].vu_r = memnew(TextureProgressBar);
channel[i].vu_r->set_fill_mode(TextureProgressBar::FILL_BOTTOM_TO_TOP);
@ -929,6 +952,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
channel[i].vu_r->set_min(-80);
channel[i].vu_r->set_max(24);
channel[i].vu_r->set_step(0.1);
channel[i].vu_r->set_accessibility_name(vformat(TTR("Channel %d, Right VU"), i));
channel[i].peak_l = 0.0f;
channel[i].peak_r = 0.0f;
@ -944,6 +968,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
hb->add_child(scale);
effects = memnew(Tree);
effects->set_accessibility_name(TTRC("Effects"));
effects->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
effects->set_hide_root(true);
effects->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
@ -963,6 +988,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
effects->connect(SceneStringName(gui_input), callable_mp(this, &EditorAudioBus::_effects_gui_input));
send = memnew(OptionButton);
send->set_accessibility_name(TTRC("Send"));
send->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
send->set_clip_text(true);
send->connect(SceneStringName(item_selected), callable_mp(this, &EditorAudioBus::_send_selected));
@ -974,8 +1000,8 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
effect_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Don't translate class names.
effect_options->connect("index_pressed", callable_mp(this, &EditorAudioBus::_effect_add));
add_child(effect_options);
List<StringName> effect_list;
ClassDB::get_inheriters_from_class("AudioEffect", &effect_list);
LocalVector<StringName> effect_list;
ClassDB::get_inheriters_from_class("AudioEffect", effect_list);
effect_list.sort_custom<StringName::AlphCompare>();
for (const StringName &E : effect_list) {
if (!ClassDB::can_instantiate(E) || ClassDB::is_virtual(E)) {
@ -992,6 +1018,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
bus_options->set_h_size_flags(SIZE_SHRINK_END);
bus_options->set_anchor(SIDE_RIGHT, 0.0);
bus_options->set_tooltip_text(TTR("Bus Options"));
bus_options->set_accessibility_name(TTRC("Bus Options"));
hbc->add_child(bus_options);
bus_popup = bus_options->get_popup();
@ -1050,9 +1077,6 @@ void EditorAudioBusDrop::_bind_methods() {
ADD_SIGNAL(MethodInfo("dropped"));
}
EditorAudioBusDrop::EditorAudioBusDrop() {
}
void EditorAudioBuses::_rebuild_buses() {
for (int i = bus_hb->get_child_count() - 1; i >= 0; i--) {
EditorAudioBus *audio_bus = Object::cast_to<EditorAudioBus>(bus_hb->get_child(i));
@ -1118,9 +1142,8 @@ void EditorAudioBuses::_notification(int p_what) {
}
}
AudioServer::get_singleton()->set_edited(false);
if (edited) {
AudioServer::get_singleton()->set_edited(false);
save_timer->start();
}
} break;
@ -1409,9 +1432,6 @@ AudioBusesEditorPlugin::AudioBusesEditorPlugin(EditorAudioBuses *p_node) {
audio_bus_editor = p_node;
}
AudioBusesEditorPlugin::~AudioBusesEditorPlugin() {
}
void EditorAudioMeterNotches::add_notch(float p_normalized_offset, float p_db_value, bool p_render_value) {
notches.push_back(AudioNotch(p_normalized_offset, p_db_value, p_render_value));
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_AUDIO_BUSES_H
#define EDITOR_AUDIO_BUSES_H
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/box_container.h"
@ -146,9 +145,6 @@ class EditorAudioBusDrop : public Control {
protected:
static void _bind_methods();
void _notification(int p_what);
public:
EditorAudioBusDrop();
};
class EditorAudioBuses : public VBoxContainer {
@ -263,9 +259,6 @@ private:
static void _bind_methods();
void _notification(int p_what);
void _draw_audio_notches();
public:
EditorAudioMeterNotches() {}
};
class AudioBusesEditorPlugin : public EditorPlugin {
@ -281,7 +274,4 @@ public:
virtual void make_visible(bool p_visible) override;
AudioBusesEditorPlugin(EditorAudioBuses *p_node);
~AudioBusesEditorPlugin();
};
#endif // EDITOR_AUDIO_BUSES_H

View file

@ -187,7 +187,7 @@ void EditorAutoloadSettings::_autoload_edited() {
if (column == 0) {
String name = ti->get_text(0);
String old_name = selected_autoload.get_slice("/", 1);
String old_name = selected_autoload.get_slicec('/', 1);
if (name == old_name) {
return;
@ -242,7 +242,7 @@ void EditorAutoloadSettings::_autoload_edited() {
String scr_path = GLOBAL_GET(base);
if (scr_path.begins_with("*")) {
scr_path = scr_path.substr(1, scr_path.length());
scr_path = scr_path.substr(1);
}
// Singleton autoloads are represented with a leading "*" in their path.
@ -349,11 +349,7 @@ void EditorAutoloadSettings::_autoload_activated() {
}
void EditorAutoloadSettings::_autoload_open(const String &fpath) {
if (ResourceLoader::get_resource_type(fpath) == "PackedScene") {
EditorNode::get_singleton()->open_request(fpath);
} else {
EditorNode::get_singleton()->load_resource(fpath);
}
EditorNode::get_singleton()->load_scene_or_resource(fpath);
ProjectSettingsEditor::get_singleton()->hide();
}
@ -483,7 +479,7 @@ void EditorAutoloadSettings::update_autoload() {
continue;
}
String name = pi.name.get_slice("/", 1);
String name = pi.name.get_slicec('/', 1);
String scr_path = GLOBAL_GET(pi.name);
if (name.is_empty()) {
@ -494,7 +490,7 @@ void EditorAutoloadSettings::update_autoload() {
info.is_singleton = scr_path.begins_with("*");
if (info.is_singleton) {
scr_path = scr_path.substr(1, scr_path.length());
scr_path = scr_path.substr(1);
}
info.name = name;
@ -625,7 +621,7 @@ Variant EditorAutoloadSettings::get_drag_data_fw(const Point2 &p_point, Control
next = tree->get_next_selected(next);
}
if (autoloads.size() == 0 || autoloads.size() == autoload_cache.size()) {
if (autoloads.is_empty() || autoloads.size() == autoload_cache.size()) {
return Variant();
}
@ -663,13 +659,13 @@ bool EditorAutoloadSettings::can_drop_data_fw(const Point2 &p_point, const Varia
}
if (drop_data.has("type")) {
TreeItem *ti = tree->get_item_at_position(p_point);
TreeItem *ti = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_selected() : tree->get_item_at_position(p_point);
if (!ti) {
return false;
}
int section = tree->get_drop_section_at_position(p_point);
int section = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_drop_section_at_position(tree->get_item_rect(ti).position) : tree->get_drop_section_at_position(p_point);
return section >= -1;
}
@ -678,13 +674,13 @@ bool EditorAutoloadSettings::can_drop_data_fw(const Point2 &p_point, const Varia
}
void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_control) {
TreeItem *ti = tree->get_item_at_position(p_point);
TreeItem *ti = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_selected() : tree->get_item_at_position(p_point);
if (!ti) {
return;
}
int section = tree->get_drop_section_at_position(p_point);
int section = (p_point == Vector2(Math::INF, Math::INF)) ? tree->get_drop_section_at_position(tree->get_item_rect(ti).position) : tree->get_drop_section_at_position(p_point);
if (section < -1) {
return;
@ -862,7 +858,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
continue;
}
String name = pi.name.get_slice("/", 1);
String name = pi.name.get_slicec('/', 1);
String scr_path = GLOBAL_GET(pi.name);
if (name.is_empty()) {
@ -873,7 +869,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
info.is_singleton = scr_path.begins_with("*");
if (info.is_singleton) {
scr_path = scr_path.substr(1, scr_path.length());
scr_path = scr_path.substr(1);
}
info.name = name;
@ -905,6 +901,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
autoload_add_path = memnew(LineEdit);
hbc->add_child(autoload_add_path);
autoload_add_path->set_accessibility_name(TTRC("Autoload Path"));
autoload_add_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
autoload_add_path->set_clear_button_enabled(true);
autoload_add_path->set_placeholder(vformat(TTR("Set path or press \"%s\" to create a script."), TTR("Add")));
@ -912,6 +909,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
browse_button = memnew(Button);
hbc->add_child(browse_button);
browse_button->set_accessibility_name(TTRC("Select Autoload Path"));
browse_button->connect(SceneStringName(pressed), callable_mp(this, &EditorAutoloadSettings::_browse_autoload_add_path));
file_dialog = memnew(EditorFileDialog);
@ -929,6 +927,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
hbc->add_child(l);
autoload_add_name = memnew(LineEdit);
autoload_add_name->set_accessibility_name(TTRC("Node Name"));
autoload_add_name->set_h_size_flags(SIZE_EXPAND_FILL);
autoload_add_name->connect(SceneStringName(text_submitted), callable_mp(this, &EditorAutoloadSettings::_autoload_text_submitted));
autoload_add_name->connect(SceneStringName(text_changed), callable_mp(this, &EditorAutoloadSettings::_autoload_text_changed));
@ -942,6 +941,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
hbc->add_child(add_autoload);
tree = memnew(Tree);
tree->set_accessibility_name(TTRC("Autoloads"));
tree->set_hide_root(true);
tree->set_select_mode(Tree::SELECT_MULTI);
tree->set_allow_reselect(true);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_AUTOLOAD_SETTINGS_H
#define EDITOR_AUTOLOAD_SETTINGS_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -114,5 +113,3 @@ public:
EditorAutoloadSettings();
~EditorAutoloadSettings();
};
#endif // EDITOR_AUTOLOAD_SETTINGS_H

View file

@ -46,7 +46,7 @@ const char *EditorBuildProfile::build_option_identifiers[BUILD_OPTION_MAX] = {
"disable_2d_physics",
"disable_3d_physics",
"disable_navigation",
"openxr",
"disable_xr",
"rendering_device", // FIXME: there's no scons option to disable rendering device
"opengl3",
"vulkan",
@ -82,7 +82,7 @@ const bool EditorBuildProfile::build_option_disable_values[BUILD_OPTION_MAX] = {
true, // PHYSICS_2D
true, // PHYSICS_3D
true, // NAVIGATION
false, // XR
true, // XR
false, // RENDERING_DEVICE
false, // OPENGL
false, // VULKAN
@ -297,11 +297,9 @@ Error EditorBuildProfile::load_from_file(const String &p_path) {
if (data.has("disabled_build_options")) {
Dictionary disabled_build_options_arr = data["disabled_build_options"];
List<Variant> keys;
disabled_build_options_arr.get_key_list(&keys);
for (const Variant &K : keys) {
String key = K;
for (const KeyValue<Variant, Variant> &kv : disabled_build_options_arr) {
String key = kv.key;
for (int i = 0; i < BUILD_OPTION_MAX; i++) {
String f = build_option_identifiers[i];
@ -810,6 +808,7 @@ EditorBuildProfileManager::EditorBuildProfileManager() {
HBoxContainer *path_hbc = memnew(HBoxContainer);
profile_path = memnew(LineEdit);
path_hbc->add_child(profile_path);
profile_path->set_accessibility_name(TTRC("Profile Path"));
profile_path->set_editable(true);
profile_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@ -868,7 +867,7 @@ EditorBuildProfileManager::EditorBuildProfileManager() {
import_profile = memnew(EditorFileDialog);
add_child(import_profile);
import_profile->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
import_profile->add_filter("*.build", TTR("Engine Compilation Profile"));
import_profile->add_filter("*.gdbuild,*.build", TTR("Engine Compilation Profile"));
import_profile->connect("files_selected", callable_mp(this, &EditorBuildProfileManager::_import_profile));
import_profile->set_title(TTR("Load Profile"));
import_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
@ -876,12 +875,13 @@ EditorBuildProfileManager::EditorBuildProfileManager() {
export_profile = memnew(EditorFileDialog);
add_child(export_profile);
export_profile->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
export_profile->add_filter("*.build", TTR("Engine Compilation Profile"));
export_profile->add_filter("*.gdbuild,*.build", TTR("Engine Compilation Profile"));
export_profile->connect("file_selected", callable_mp(this, &EditorBuildProfileManager::_export_profile));
export_profile->set_title(TTR("Export Profile"));
export_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
force_detect_classes = memnew(LineEdit);
force_detect_classes->set_accessibility_name(TTRC("Forced Classes"));
main_vbc->add_margin_child(TTR("Forced Classes on Detect:"), force_detect_classes);
force_detect_classes->connect(SceneStringName(text_changed), callable_mp(this, &EditorBuildProfileManager::_force_detect_classes_changed));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_BUILD_PROFILE_H
#define EDITOR_BUILD_PROFILE_H
#pragma once
#include "core/object/ref_counted.h"
#include "editor/editor_help.h"
@ -185,5 +184,3 @@ public:
static EditorBuildProfileManager *get_singleton() { return singleton; }
EditorBuildProfileManager();
};
#endif // EDITOR_BUILD_PROFILE_H

View file

@ -2,141 +2,95 @@
import os
import os.path
import shutil
import subprocess
import tempfile
import uuid
import zlib
from methods import print_warning
import methods
def make_doc_header(target, source, env):
dst = str(target[0])
with open(dst, "w", encoding="utf-8", newline="\n") as g:
buf = ""
docbegin = ""
docend = ""
for src in source:
src = str(src)
if not src.endswith(".xml"):
continue
with open(src, "r", encoding="utf-8") as f:
content = f.read()
buf += content
buffer = b"".join([methods.get_buffer(src) for src in map(str, source)])
decomp_size = len(buffer)
buffer = methods.compress_buffer(buffer)
buf = (docbegin + buf + docend).encode("utf-8")
decomp_size = len(buf)
# Use maximum zlib compression level to further reduce file size
# (at the cost of initial build times).
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
g.write("#ifndef _DOC_DATA_RAW_H\n")
g.write("#define _DOC_DATA_RAW_H\n")
g.write('static const char *_doc_data_hash = "' + str(hash(buf)) + '";\n')
g.write("static const int _doc_data_compressed_size = " + str(len(buf)) + ";\n")
g.write("static const int _doc_data_uncompressed_size = " + str(decomp_size) + ";\n")
g.write("static const unsigned char _doc_data_compressed[] = {\n")
for i in range(len(buf)):
g.write("\t" + str(buf[i]) + ",\n")
g.write("};\n")
g.write("#endif")
with methods.generated_wrapper(str(target[0])) as file:
file.write(f"""\
inline constexpr const char *_doc_data_hash = "{hash(buffer)}";
inline constexpr int _doc_data_compressed_size = {len(buffer)};
inline constexpr int _doc_data_uncompressed_size = {decomp_size};
inline constexpr const unsigned char _doc_data_compressed[] = {{
{methods.format_buffer(buffer, 1)}
}};
""")
def make_translations_header(target, source, env, category):
dst = str(target[0])
def make_translations_header(target, source, env):
category = os.path.basename(str(target[0])).split("_")[0]
sorted_paths = sorted([src.abspath for src in source], key=lambda path: os.path.splitext(os.path.basename(path))[0])
with open(dst, "w", encoding="utf-8", newline="\n") as g:
g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
g.write("#ifndef _{}_TRANSLATIONS_H\n".format(category.upper()))
g.write("#define _{}_TRANSLATIONS_H\n".format(category.upper()))
xl_names = []
msgfmt = env.Detect("msgfmt")
if not msgfmt:
methods.print_warning("msgfmt not found, using .po files instead of .mo")
sorted_paths = sorted([str(x) for x in source], key=lambda path: os.path.splitext(os.path.basename(path))[0])
msgfmt_available = shutil.which("msgfmt") is not None
if not msgfmt_available:
print_warning("msgfmt is not found, using .po files instead of .mo")
xl_names = []
for i in range(len(sorted_paths)):
name = os.path.splitext(os.path.basename(sorted_paths[i]))[0]
with methods.generated_wrapper(str(target[0])) as file:
for path in sorted_paths:
name = os.path.splitext(os.path.basename(path))[0]
# msgfmt erases non-translated messages, so avoid using it if exporting the POT.
if msgfmt_available and name != category:
if msgfmt and name != category:
mo_path = os.path.join(tempfile.gettempdir(), uuid.uuid4().hex + ".mo")
cmd = "msgfmt " + sorted_paths[i] + " --no-hash -o " + mo_path
cmd = f"{msgfmt} {path} --no-hash -o {mo_path}"
try:
subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
with open(mo_path, "rb") as f:
buf = f.read()
buffer = methods.get_buffer(mo_path)
except OSError as e:
print_warning(
methods.print_warning(
"msgfmt execution failed, using .po file instead of .mo: path=%r; [%s] %s"
% (sorted_paths[i], e.__class__.__name__, e)
% (path, e.__class__.__name__, e)
)
with open(sorted_paths[i], "rb") as f:
buf = f.read()
buffer = methods.get_buffer(path)
finally:
try:
os.remove(mo_path)
if os.path.exists(mo_path):
os.remove(mo_path)
except OSError as e:
# Do not fail the entire build if it cannot delete a temporary file.
print_warning(
methods.print_warning(
"Could not delete temporary .mo file: path=%r; [%s] %s" % (mo_path, e.__class__.__name__, e)
)
else:
with open(sorted_paths[i], "rb") as f:
buf = f.read()
buffer = methods.get_buffer(path)
if name == category:
name = "source"
decomp_size = len(buf)
# Use maximum zlib compression level to further reduce file size
# (at the cost of initial build times).
buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
decomp_size = len(buffer)
buffer = methods.compress_buffer(buffer)
g.write("static const unsigned char _{}_translation_{}_compressed[] = {{\n".format(category, name))
for j in range(len(buf)):
g.write("\t" + str(buf[j]) + ",\n")
file.write(f"""\
inline constexpr const unsigned char _{category}_translation_{name}_compressed[] = {{
{methods.format_buffer(buffer, 1)}
}};
g.write("};\n")
""")
xl_names.append([name, len(buf), str(decomp_size)])
xl_names.append([name, len(buffer), decomp_size])
file.write(f"""\
struct {category.capitalize()}TranslationList {{
const char* lang;
int comp_size;
int uncomp_size;
const unsigned char* data;
}};
inline constexpr {category.capitalize()}TranslationList _{category}_translations[] = {{
""")
g.write("struct {}TranslationList {{\n".format(category.capitalize()))
g.write("\tconst char* lang;\n")
g.write("\tint comp_size;\n")
g.write("\tint uncomp_size;\n")
g.write("\tconst unsigned char* data;\n")
g.write("};\n\n")
g.write("static {}TranslationList _{}_translations[] = {{\n".format(category.capitalize(), category))
for x in xl_names:
g.write(
'\t{{ "{}", {}, {}, _{}_translation_{}_compressed }},\n'.format(
x[0], str(x[1]), str(x[2]), category, x[0]
)
)
g.write("\t{nullptr, 0, 0, nullptr}\n")
g.write("};\n")
file.write(f'\t{{ "{x[0]}", {x[1]}, {x[2]}, _{category}_translation_{x[0]}_compressed }},\n')
g.write("#endif")
def make_editor_translations_header(target, source, env):
make_translations_header(target, source, env, "editor")
def make_property_translations_header(target, source, env):
make_translations_header(target, source, env, "property")
def make_doc_translations_header(target, source, env):
make_translations_header(target, source, env, "doc")
def make_extractable_translations_header(target, source, env):
make_translations_header(target, source, env, "extractable")
file.write("""\
{ nullptr, 0, 0, nullptr },
};
""")

View file

@ -112,7 +112,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) {
const int entry_limit = MIN(entries.size(), 300);
for (int i = 0; i < entry_limit; i++) {
String section_name = entries[i].key_name.get_slice("/", 0);
String section_name = entries[i].key_name.get_slicec('/', 0);
TreeItem *section;
if (sections.has(section_name)) {
@ -294,11 +294,10 @@ void EditorCommandPalette::register_shortcuts_as_command() {
// Load command use history.
Dictionary command_history = EditorSettings::get_singleton()->get_project_metadata("command_palette", "command_history", Dictionary());
Array history_entries = command_history.keys();
for (int i = 0; i < history_entries.size(); i++) {
const String &history_key = history_entries[i];
for (const KeyValue<Variant, Variant> &history_kv : command_history) {
const String &history_key = history_kv.key;
if (commands.has(history_key)) {
commands[history_key].last_used = command_history[history_key];
commands[history_key].last_used = history_kv.value;
}
}
}
@ -345,6 +344,7 @@ EditorCommandPalette::EditorCommandPalette() {
command_search_box = memnew(LineEdit);
command_search_box->set_placeholder(TTR("Filter Commands"));
command_search_box->set_accessibility_name(TTRC("Filter Commands"));
command_search_box->connect(SceneStringName(gui_input), callable_mp(this, &EditorCommandPalette::_sbox_input));
command_search_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorCommandPalette::_update_command_search));
command_search_box->set_v_size_flags(Control::SIZE_EXPAND_FILL);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_COMMAND_PALETTE_H
#define EDITOR_COMMAND_PALETTE_H
#pragma once
#include "core/input/shortcut.h"
#include "scene/gui/dialogs.h"
@ -103,5 +102,3 @@ public:
Ref<Shortcut> ED_SHORTCUT_AND_COMMAND(const String &p_path, const String &p_name, Key p_keycode = Key::NONE, String p_command = "");
Ref<Shortcut> ED_SHORTCUT_ARRAY_AND_COMMAND(const String &p_path, const String &p_name, const PackedInt32Array &p_keycodes, String p_command = "");
#endif // EDITOR_COMMAND_PALETTE_H

View file

@ -336,12 +336,8 @@ void EditorData::set_editor_plugin_states(const Dictionary &p_states) {
return;
}
List<Variant> keys;
p_states.get_key_list(&keys);
List<Variant>::Element *E = keys.front();
for (; E; E = E->next()) {
String name = E->get();
for (const KeyValue<Variant, Variant> &kv : p_states) {
String name = kv.key;
int idx = -1;
for (int i = 0; i < editor_plugins.size(); i++) {
if (editor_plugins[i]->get_plugin_name() == name) {
@ -353,7 +349,7 @@ void EditorData::set_editor_plugin_states(const Dictionary &p_states) {
if (idx == -1) {
continue;
}
editor_plugins[idx]->set_state(p_states[name]);
editor_plugins[idx]->set_state(kv.value);
}
}
@ -602,8 +598,7 @@ void EditorData::instantiate_object_properties(Object *p_object) {
List<PropertyInfo> pinfo;
p_object->get_property_list(&pinfo);
for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
PropertyInfo pi = E->get();
for (const PropertyInfo &pi : pinfo) {
if (pi.type == Variant::OBJECT && pi.usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) {
Object *prop = ClassDB::instantiate(pi.class_name);
p_object->set(pi.name, prop);
@ -1071,12 +1066,10 @@ void EditorData::script_class_load_icon_paths() {
#ifndef DISABLE_DEPRECATED
if (ProjectSettings::get_singleton()->has_setting("_global_script_class_icons")) {
Dictionary d = GLOBAL_GET("_global_script_class_icons");
List<Variant> keys;
d.get_key_list(&keys);
for (const Variant &E : keys) {
String name = E.operator String();
_script_class_icon_paths[name] = d[name];
for (const KeyValue<Variant, Variant> &kv : d) {
String name = kv.key.operator String();
_script_class_icon_paths[name] = kv.value;
String path = ScriptServer::get_global_class_path(name);
script_class_set_name(path, name);
@ -1258,7 +1251,10 @@ void EditorSelection::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_node", "node"), &EditorSelection::add_node);
ClassDB::bind_method(D_METHOD("remove_node", "node"), &EditorSelection::remove_node);
ClassDB::bind_method(D_METHOD("get_selected_nodes"), &EditorSelection::get_selected_nodes);
ClassDB::bind_method(D_METHOD("get_transformable_selected_nodes"), &EditorSelection::_get_transformable_selected_nodes);
ClassDB::bind_method(D_METHOD("get_top_selected_nodes"), &EditorSelection::get_top_selected_nodes);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("get_transformable_selected_nodes"), &EditorSelection::get_top_selected_nodes);
#endif // DISABLE_DEPRECATED
ADD_SIGNAL(MethodInfo("selection_changed"));
}
@ -1271,7 +1267,7 @@ void EditorSelection::_update_node_list() {
return;
}
selected_node_list.clear();
top_selected_node_list.clear();
// If the selection does not have the parent of the selected node, then add the node to the node list.
// However, if the parent is already selected, then adding this node is redundant as
@ -1291,7 +1287,7 @@ void EditorSelection::_update_node_list() {
if (skip) {
continue;
}
selected_node_list.push_back(E.key);
top_selected_node_list.push_back(E.key);
}
node_list_changed = true;
@ -1315,10 +1311,10 @@ void EditorSelection::_emit_change() {
emitted = false;
}
TypedArray<Node> EditorSelection::_get_transformable_selected_nodes() {
TypedArray<Node> EditorSelection::get_top_selected_nodes() {
TypedArray<Node> ret;
for (const Node *E : selected_node_list) {
for (const Node *E : top_selected_node_list) {
ret.push_back(E);
}
@ -1335,13 +1331,13 @@ TypedArray<Node> EditorSelection::get_selected_nodes() {
return ret;
}
List<Node *> &EditorSelection::get_selected_node_list() {
const List<Node *> &EditorSelection::get_top_selected_node_list() {
if (changed) {
update();
} else {
_update_node_list();
}
return selected_node_list;
return top_selected_node_list;
}
List<Node *> EditorSelection::get_full_selected_node_list() {
@ -1362,9 +1358,6 @@ void EditorSelection::clear() {
node_list_changed = true;
}
EditorSelection::EditorSelection() {
}
EditorSelection::~EditorSelection() {
clear();
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DATA_H
#define EDITOR_DATA_H
#pragma once
#include "core/templates/list.h"
#include "scene/resources/texture.h"
@ -287,10 +286,9 @@ class EditorSelection : public Object {
// Editor plugins which are related to selection.
List<Object *> editor_plugins;
List<Node *> selected_node_list;
List<Node *> top_selected_node_list;
void _update_node_list();
TypedArray<Node> _get_transformable_selected_nodes();
void _emit_change();
protected:
@ -315,18 +313,17 @@ public:
void update();
void clear();
// Returns all the selected nodes.
TypedArray<Node> get_selected_nodes();
// Returns only the top level selected nodes.
// That is, if the selection includes some node and a child of that node, only the parent is returned.
List<Node *> &get_selected_node_list();
const List<Node *> &get_top_selected_node_list();
// Same as get_top_selected_node_list but returns a copy in a TypedArray for binding to scripts.
TypedArray<Node> get_top_selected_nodes();
// Returns all the selected nodes (list version of "get_selected_nodes").
List<Node *> get_full_selected_node_list();
// Same as get_full_selected_node_list but returns a copy in a TypedArray for binding to scripts.
TypedArray<Node> get_selected_nodes();
// Returns the map of selected objects and their metadata.
HashMap<Node *, Object *> &get_selection() { return selection; }
EditorSelection();
~EditorSelection();
};
#endif // EDITOR_DATA_H

View file

@ -158,17 +158,20 @@ void EditorDockManager::_update_layout() {
return;
}
dock_context_popup->docks_updated();
_update_docks_menu();
update_docks_menu();
EditorNode::get_singleton()->save_editor_layout_delayed();
}
void EditorDockManager::_update_docks_menu() {
void EditorDockManager::update_docks_menu() {
docks_menu->clear();
docks_menu->reset_size();
const Ref<Texture2D> default_icon = docks_menu->get_editor_theme_icon(SNAME("Window"));
const Color closed_icon_color_mod = Color(1, 1, 1, 0.5);
bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU);
bool dark_mode = DisplayServer::get_singleton()->is_dark_mode_supported() && DisplayServer::get_singleton()->is_dark_mode();
// Add docks.
docks_menu_docks.clear();
int id = 0;
@ -182,7 +185,7 @@ void EditorDockManager::_update_docks_menu() {
} else {
docks_menu->add_item(dock.value.title, id);
}
const Ref<Texture2D> icon = dock.value.icon_name ? docks_menu->get_editor_theme_icon(dock.value.icon_name) : dock.value.icon;
const Ref<Texture2D> icon = dock.value.icon_name ? docks_menu->get_editor_theme_native_menu_icon(dock.value.icon_name, global_menu, dark_mode) : dock.value.icon;
docks_menu->set_item_icon(id, icon.is_valid() ? icon : default_icon);
if (!dock.value.open) {
docks_menu->set_item_icon_modulate(id, closed_icon_color_mod);
@ -613,7 +616,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
hsplits[i]->set_split_offset(ofs * EDSCALE);
}
_update_docks_menu();
update_docks_menu();
}
void EditorDockManager::bottom_dock_show_placement_popup(const Rect2i &p_position, Control *p_dock) {
@ -848,11 +851,13 @@ EditorDockManager::EditorDockManager() {
docks_menu = memnew(PopupMenu);
docks_menu->set_hide_on_item_selection(false);
docks_menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorDockManager::_docks_menu_option));
EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorDockManager::_update_docks_menu));
EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorDockManager::update_docks_menu));
}
void DockContextPopup::_notification(int p_what) {
switch (p_what) {
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
if (make_float_button) {
make_float_button->set_button_icon(get_editor_theme_icon(SNAME("MakeFloating")));
@ -1087,6 +1092,7 @@ DockContextPopup::DockContextPopup() {
HBoxContainer *header_hb = memnew(HBoxContainer);
tab_move_left_button = memnew(Button);
tab_move_left_button->set_accessibility_name(TTRC("Move Tab Left"));
tab_move_left_button->set_flat(true);
tab_move_left_button->set_focus_mode(Control::FOCUS_NONE);
tab_move_left_button->connect(SceneStringName(pressed), callable_mp(this, &DockContextPopup::_tab_move_left));
@ -1099,6 +1105,7 @@ DockContextPopup::DockContextPopup() {
header_hb->add_child(position_label);
tab_move_right_button = memnew(Button);
tab_move_right_button->set_accessibility_name(TTRC("Move Tab Right"));
tab_move_right_button->set_flat(true);
tab_move_right_button->set_focus_mode(Control::FOCUS_NONE);
tab_move_right_button->connect(SceneStringName(pressed), callable_mp(this, &DockContextPopup::_tab_move_right));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DOCK_MANAGER_H
#define EDITOR_DOCK_MANAGER_H
#pragma once
#include "scene/gui/popup.h"
#include "scene/gui/split_container.h"
@ -113,7 +112,6 @@ private:
void _dock_container_update_visibility(TabContainer *p_dock_container);
void _update_layout();
void _update_docks_menu();
void _docks_menu_option(int p_id);
void _window_close_request(WindowWrapper *p_wrapper);
@ -133,6 +131,7 @@ private:
public:
static EditorDockManager *get_singleton() { return singleton; }
void update_docks_menu();
void update_tab_styles();
void set_tab_icon_max_width(int p_max_width);
@ -207,5 +206,3 @@ public:
DockContextPopup();
};
#endif // EDITOR_DOCK_MANAGER_H

View file

@ -261,7 +261,7 @@ Error EditorFeatureProfile::load_from_file(const String &p_path) {
Array disabled_properties_arr = data["disabled_properties"];
for (int i = 0; i < disabled_properties_arr.size(); i++) {
String s = disabled_properties_arr[i];
set_disable_class_property(s.get_slice(":", 0), s.get_slice(":", 1), true);
set_disable_class_property(s.get_slicec(':', 0), s.get_slicec(':', 1), true);
}
}
@ -924,6 +924,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
HBoxContainer *name_hbc = memnew(HBoxContainer);
current_profile_name = memnew(LineEdit);
name_hbc->add_child(current_profile_name);
current_profile_name->set_accessibility_name(TTRC("Current Profile"));
current_profile_name->set_text(TTR("(none)"));
current_profile_name->set_editable(false);
current_profile_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@ -938,6 +939,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
HBoxContainer *profiles_hbc = memnew(HBoxContainer);
profile_list = memnew(OptionButton);
profile_list->set_accessibility_name(TTRC("Profiles"));
profile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
profile_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
profiles_hbc->add_child(profile_list);
@ -1031,6 +1033,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
new_profile_name = memnew(LineEdit);
new_profile_vb->add_child(new_profile_name);
new_profile_name->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
new_profile_name->set_accessibility_name(TTRC("Profile Name"));
add_child(new_profile_dialog);
new_profile_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorFeatureProfileManager::_create_new_profile));
new_profile_dialog->register_text_enter(new_profile_name);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_FEATURE_PROFILE_H
#define EDITOR_FEATURE_PROFILE_H
#pragma once
#include "core/object/ref_counted.h"
#include "editor/editor_help.h"
@ -185,5 +184,3 @@ public:
static EditorFeatureProfileManager *get_singleton() { return singleton; }
EditorFeatureProfileManager();
};
#endif // EDITOR_FEATURE_PROFILE_H

View file

@ -447,8 +447,8 @@ void EditorFileSystem::_scan_filesystem() {
name = cpath.path_join(name);
FileCache fc;
fc.type = split[1].get_slice("/", 0);
fc.resource_script_class = split[1].get_slice("/", 1);
fc.type = split[1].get_slicec('/', 0);
fc.resource_script_class = split[1].get_slicec('/', 1);
fc.uid = split[2].to_int();
fc.modification_time = split[3].to_int();
fc.import_modification_time = split[4].to_int();
@ -802,7 +802,7 @@ Vector<String> EditorFileSystem::_get_import_dest_paths(const String &p_path) {
}
bool EditorFileSystem::_scan_import_support(const Vector<String> &reimports) {
if (import_support_queries.size() == 0) {
if (import_support_queries.is_empty()) {
return false;
}
HashMap<String, int> import_support_test;
@ -818,7 +818,7 @@ bool EditorFileSystem::_scan_import_support(const Vector<String> &reimports) {
}
}
if (import_support_test.size() == 0) {
if (import_support_test.is_empty()) {
return false; //well nothing to do
}
@ -905,7 +905,7 @@ bool EditorFileSystem::_update_scan_actions() {
if (existing_id != ResourceUID::INVALID_ID) {
const String old_path = ResourceUID::get_singleton()->get_id_path(existing_id);
if (old_path != new_file_path && FileAccess::exists(old_path)) {
const ResourceUID::ID new_id = ResourceUID::get_singleton()->create_id();
const ResourceUID::ID new_id = ResourceUID::get_singleton()->create_id_for_path(new_file_path);
ResourceUID::get_singleton()->add_id(new_id, new_file_path);
ResourceSaver::set_uid(new_file_path, new_id);
WARN_PRINT(vformat("Duplicate UID detected for Resource at \"%s\".\nOld Resource path: \"%s\". The new file UID was changed automatically.", new_file_path, old_path));
@ -913,6 +913,12 @@ bool EditorFileSystem::_update_scan_actions() {
// Re-assign the UID to file, just in case it was pulled from cache.
ResourceSaver::set_uid(new_file_path, existing_id);
}
} else if (ResourceLoader::should_create_uid_file(new_file_path)) {
Ref<FileAccess> f = FileAccess::open(new_file_path + ".uid", FileAccess::WRITE);
if (f.is_valid()) {
ia.new_file->uid = ResourceUID::get_singleton()->create_id_for_path(new_file_path);
f->store_line(ResourceUID::get_singleton()->id_to_text(ia.new_file->uid));
}
}
if (ClassDB::is_parent_class(ia.new_file->type, SNAME("Script"))) {
@ -927,15 +933,16 @@ bool EditorFileSystem::_update_scan_actions() {
int idx = ia.dir->find_file_index(ia.file);
ERR_CONTINUE(idx == -1);
String class_name = ia.dir->files[idx]->class_info.name;
const String file_path = ia.dir->get_file_path(idx);
const String class_name = ia.dir->files[idx]->class_info.name;
if (ClassDB::is_parent_class(ia.dir->files[idx]->type, SNAME("Script"))) {
_queue_update_script_class(ia.dir->get_file_path(idx), ScriptClassInfoUpdate());
_queue_update_script_class(file_path, ScriptClassInfoUpdate());
}
if (ia.dir->files[idx]->type == SNAME("PackedScene")) {
_queue_update_scene_groups(ia.dir->get_file_path(idx));
_queue_update_scene_groups(file_path);
}
_delete_internal_files(ia.dir->files[idx]->file);
_delete_internal_files(file_path);
memdelete(ia.dir->files[idx]);
ia.dir->files.remove_at(idx);
@ -963,7 +970,7 @@ bool EditorFileSystem::_update_scan_actions() {
Vector<String> dependencies = _get_dependencies(full_path);
for (const String &dep : dependencies) {
const String &dependency_path = dep.contains("::") ? dep.get_slice("::", 0) : dep;
if (import_extensions.has(dep.get_extension())) {
if (_can_import_file(dep)) {
reimports.push_back(dependency_path);
}
}
@ -1061,6 +1068,19 @@ void EditorFileSystem::scan() {
// to be loaded to continue the scan and reimportations.
if (first_scan) {
_first_scan_filesystem();
#ifdef ANDROID_ENABLED
const String nomedia_file_path = ProjectSettings::get_singleton()->get_resource_path().path_join(".nomedia");
if (!FileAccess::exists(nomedia_file_path)) {
// Create a .nomedia file to hide assets from media apps on Android.
Ref<FileAccess> f = FileAccess::open(nomedia_file_path, FileAccess::WRITE);
if (f.is_null()) {
// .nomedia isn't so critical.
ERR_PRINT("Couldn't create .nomedia in project path.");
} else {
f->close();
}
}
#endif
}
_update_extensions();
@ -1145,15 +1165,15 @@ int EditorFileSystem::_scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da)
int nb_files_total_scan = 0;
for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
if (da->change_dir(E->get()) == OK) {
for (const String &dir : dirs) {
if (da->change_dir(dir) == OK) {
String d = da->get_current_dir();
if (d == cd || !d.begins_with(cd)) {
da->change_dir(cd); //avoid recursion
} else {
ScannedDirectory *sd = memnew(ScannedDirectory);
sd->name = E->get();
sd->name = dir;
sd->full_path = p_dir->full_path.path_join(sd->name);
nb_files_total_scan += _scan_new_dir(sd, da);
@ -1163,7 +1183,7 @@ int EditorFileSystem::_scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da)
da->change_dir("..");
}
} else {
ERR_PRINT("Cannot go into subdir '" + E->get() + "'.");
ERR_PRINT("Cannot go into subdir '" + dir + "'.");
}
}
@ -1204,7 +1224,7 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
FileCache *fc = file_cache.getptr(path);
uint64_t mt = FileAccess::get_modified_time(path);
if (import_extensions.has(ext)) {
if (_can_import_file(scan_file)) {
//is imported
uint64_t import_mt = FileAccess::get_modified_time(path + ".import");
@ -1333,7 +1353,7 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir,
Ref<FileAccess> f = FileAccess::open(path + ".uid", FileAccess::WRITE);
if (f.is_valid()) {
if (fi->uid == ResourceUID::INVALID_ID) {
fi->uid = ResourceUID::get_singleton()->create_id();
fi->uid = ResourceUID::get_singleton()->create_id_for_path(path);
} else {
WARN_PRINT(vformat("Missing .uid file for path \"%s\". The file was re-created from cache.", path));
}
@ -1494,7 +1514,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
scan_actions.push_back(ia);
}
if (import_extensions.has(ext)) {
if (_can_import_file(f)) {
//if it can be imported, and it was added, it needs to be reimported
ItemAction ia;
ia.action = ItemAction::ACTION_FILE_TEST_REIMPORT;
@ -1526,7 +1546,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr
String path = cd.path_join(p_dir->files[i]->file);
if (import_extensions.has(p_dir->files[i]->file.get_extension().to_lower())) {
if (_can_import_file(p_dir->files[i]->file)) {
// Check here if file must be imported or not.
// Same logic as in _process_file_system, the last modifications dates
// needs to be trusted to prevent reading all the .import files and the md5
@ -1846,12 +1866,12 @@ bool EditorFileSystem::_find_file(const String &p_file, EditorFileSystemDirector
if (!f.begins_with("res://")) {
return false;
}
f = f.substr(6, f.length());
f = f.replace("\\", "/");
f = f.substr(6);
f = f.replace_char('\\', '/');
Vector<String> path = f.split("/");
if (path.size() == 0) {
if (path.is_empty()) {
return false;
}
String file = path[path.size() - 1];
@ -1972,8 +1992,8 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p
return nullptr;
}
f = f.substr(6, f.length());
f = f.replace("\\", "/");
f = f.substr(6);
f = f.replace_char('\\', '/');
if (f.is_empty()) {
return filesystem;
}
@ -1984,7 +2004,7 @@ EditorFileSystemDirectory *EditorFileSystem::get_filesystem_path(const String &p
Vector<String> path = f.split("/");
if (path.size() == 0) {
if (path.is_empty()) {
return nullptr;
}
@ -2171,6 +2191,7 @@ void EditorFileSystem::_update_script_documentation() {
if (!efd || index < 0) {
// The file was removed
EditorHelp::remove_script_doc_by_path(path);
continue;
}
@ -2188,7 +2209,7 @@ void EditorFileSystem::_update_script_documentation() {
scr->reload_from_file();
}
for (const DocData::ClassDoc &cd : scr->get_documentation()) {
EditorHelp::get_doc_data()->add_doc(cd);
EditorHelp::add_doc(cd);
if (!first_scan) {
// Update the documentation in the Script Editor if it is open.
ScriptEditor::get_singleton()->update_doc(cd.name);
@ -2421,7 +2442,7 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) {
if (ResourceLoader::should_create_uid_file(file)) {
Ref<FileAccess> f = FileAccess::open(file + ".uid", FileAccess::WRITE);
if (f.is_valid()) {
const ResourceUID::ID id = ResourceUID::get_singleton()->create_id();
const ResourceUID::ID id = ResourceUID::get_singleton()->create_id_for_path(file);
ResourceUID::get_singleton()->add_id(id, file);
f->store_line(ResourceUID::get_singleton()->id_to_text(id));
fi->uid = id;
@ -2609,7 +2630,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
}
if (uid == ResourceUID::INVALID_ID) {
uid = ResourceUID::get_singleton()->create_id();
uid = ResourceUID::get_singleton()->create_id_for_path(file);
}
f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); // Store in readable format.
@ -2799,7 +2820,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
if (importer.is_null()) {
//not found by name, find by extension
importer = ResourceFormatImporter::get_singleton()->get_importer_by_extension(p_file.get_extension());
importer = ResourceFormatImporter::get_singleton()->get_importer_by_file(p_file);
load_default = true;
if (importer.is_null()) {
ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "BUG: File queued for import, but can't be imported, importer for type '" + importer_name + "' not found.");
@ -2824,16 +2845,14 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
if (load_default && ProjectSettings::get_singleton()->has_setting("importer_defaults/" + importer->get_importer_name())) {
//use defaults if exist
Dictionary d = GLOBAL_GET("importer_defaults/" + importer->get_importer_name());
List<Variant> v;
d.get_key_list(&v);
for (const Variant &E : v) {
params[E] = d[E];
for (const KeyValue<Variant, Variant> &kv : d) {
params[kv.key] = kv.value;
}
}
if (uid == ResourceUID::INVALID_ID) {
uid = ResourceUID::get_singleton()->create_id();
uid = ResourceUID::get_singleton()->create_id_for_path(p_file);
}
//finally, perform import!!
@ -3093,7 +3112,7 @@ void EditorFileSystem::_queue_refresh_filesystem() {
void EditorFileSystem::_refresh_filesystem() {
for (const ObjectID &id : folders_to_sort) {
EditorFileSystemDirectory *dir = Object::cast_to<EditorFileSystemDirectory>(ObjectDB::get_instance(id));
EditorFileSystemDirectory *dir = ObjectDB::get_instance<EditorFileSystemDirectory>(id);
if (dir) {
dir->subdirs.sort_custom<DirectoryComparator>();
}
@ -3107,8 +3126,11 @@ void EditorFileSystem::_refresh_filesystem() {
}
void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) {
ResourceLoader::set_is_import_thread(true);
int file_idx = p_import_data->reimport_from + int(p_index);
_reimport_file(p_import_data->reimport_files[file_idx].path);
ResourceLoader::set_is_import_thread(false);
p_import_data->imported_sem->post();
}
@ -3437,7 +3459,7 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p
ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND);
folders_to_sort.insert(parent->get_instance_id());
const PackedStringArray folders = p_path.trim_prefix(path).trim_suffix("/").split("/");
const PackedStringArray folders = p_path.trim_prefix(path).split("/", false);
for (const String &folder : folders) {
const int current = parent->find_dir_index(folder);
if (current > -1) {
@ -3519,14 +3541,14 @@ ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const
}
if (p_generate) {
return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
return ResourceUID::get_singleton()->create_id_for_path(p_path); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
} else {
return ResourceUID::INVALID_ID;
}
} else if (fs->files[cpos]->uid != ResourceUID::INVALID_ID) {
return fs->files[cpos]->uid;
} else if (p_generate) {
return ResourceUID::get_singleton()->create_id(); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
return ResourceUID::get_singleton()->create_id_for_path(p_path); // Just create a new one, we will be notified of save anyway and fetch the right UID at that time, to keep things simple.
} else {
return ResourceUID::INVALID_ID;
}
@ -3604,10 +3626,20 @@ void EditorFileSystem::_update_extensions() {
extensionsl.clear();
ResourceFormatImporter::get_singleton()->get_recognized_extensions(&extensionsl);
for (const String &E : extensionsl) {
import_extensions.insert(E);
import_extensions.insert(!E.begins_with(".") ? "." + E : E);
}
}
bool EditorFileSystem::_can_import_file(const String &p_file) {
for (const String &F : import_extensions) {
if (p_file.right(F.length()).nocasecmp_to(F) == 0) {
return true;
}
}
return false;
}
void EditorFileSystem::add_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query) {
ERR_FAIL_COND(import_support_queries.has(p_query));
import_support_queries.push_back(p_query);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_FILE_SYSTEM_H
#define EDITOR_FILE_SYSTEM_H
#pragma once
#include "core/io/dir_access.h"
#include "core/io/resource_importer.h"
@ -280,6 +279,7 @@ class EditorFileSystem : public Node {
bool _test_for_reimport(const String &p_path, const String &p_expected_import_md5);
bool _is_test_for_reimport_needed(const String &p_path, uint64_t p_last_modification_time, uint64_t p_modification_time, uint64_t p_last_import_modification_time, uint64_t p_import_modification_time, const Vector<String> &p_import_dest_paths);
bool _can_import_file(const String &p_path);
Vector<String> _get_import_dest_paths(const String &p_path);
bool reimport_on_missing_imported_files;
@ -424,5 +424,3 @@ public:
EditorFileSystem();
~EditorFileSystem();
};
#endif // EDITOR_FILE_SYSTEM_H

View file

@ -65,7 +65,7 @@ void EditorFolding::_set_unfolds(Object *p_object, const Vector<String> &p_unfol
const String *r = p_unfolds.ptr();
p_object->editor_clear_section_folding();
for (int i = 0; i < uc; i++) {
p_object->editor_set_section_unfold(r[i], true);
p_object->editor_set_section_unfold(r[i], true, true);
}
}
@ -294,6 +294,3 @@ void EditorFolding::unfold_scene(Node *p_scene) {
HashSet<Ref<Resource>> resources;
_do_node_unfolds(p_scene, p_scene, resources);
}
EditorFolding::EditorFolding() {
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_FOLDING_H
#define EDITOR_FOLDING_H
#pragma once
#include "scene/main/node.h"
@ -52,8 +51,4 @@ public:
void unfold_scene(Node *p_scene);
bool has_folding_data(const String &p_path);
EditorFolding();
};
#endif // EDITOR_FOLDING_H

View file

@ -38,8 +38,9 @@
#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/string/string_builder.h"
#include "core/version_generated.gen.h"
#include "core/version.h"
#include "editor/doc_data_compressed.gen.h"
#include "editor/editor_file_system.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
@ -66,7 +67,7 @@
#include "modules/mono/csharp_script.h"
#endif
#define CONTRIBUTE_URL vformat("%s/contributing/documentation/updating_the_class_reference.html", VERSION_DOCS_URL)
#define CONTRIBUTE_URL vformat("%s/contributing/documentation/updating_the_class_reference.html", GODOT_VERSION_DOCS_URL)
#ifdef MODULE_MONO_ENABLED
// Sync with the types mentioned in https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_differences.html
@ -102,7 +103,8 @@ const Vector<String> classes_with_csharp_differences = {
};
#endif
static const String nbsp = String::chr(160);
static const char32_t nbsp_chr = 160;
static const String nbsp = String::chr(nbsp_chr);
static const String nbsp_equal_nbsp = nbsp + "=" + nbsp;
static const String colon_nbsp = ":" + nbsp;
@ -120,7 +122,7 @@ const Vector<String> packed_array_types = {
};
static String _replace_nbsp_with_space(const String &p_string) {
return p_string.replace(nbsp, " ");
return p_string.replace_char(nbsp_chr, ' ');
}
static String _fix_constant(const String &p_constant) {
@ -192,37 +194,6 @@ static String _contextualize_class_specifier(const String &p_class_specifier, co
/// EditorHelp ///
// TODO: This is sometimes used directly as `doc->something`, other times as `EditorHelp::get_doc_data()`, which is thread-safe.
// Might this be a problem?
DocTools *EditorHelp::doc = nullptr;
DocTools *EditorHelp::ext_doc = nullptr;
int EditorHelp::doc_generation_count = 0;
String EditorHelp::doc_version_hash;
Thread EditorHelp::worker_thread;
static bool _attempt_doc_load(const String &p_class) {
// Docgen always happens in the outer-most class: it also generates docs for inner classes.
const String outer_class = p_class.get_slicec('.', 0);
if (!ScriptServer::is_global_class(outer_class)) {
return false;
}
// `ResourceLoader` is used in order to have a script-agnostic way to load scripts.
// This forces GDScript to compile the code, which is unnecessary for docgen, but it's a good compromise right now.
const Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(outer_class), outer_class);
if (script.is_valid()) {
const Vector<DocData::ClassDoc> docs = script->get_documentation();
for (int j = 0; j < docs.size(); j++) {
const DocData::ClassDoc &doc = docs.get(j);
EditorHelp::get_doc_data()->add_doc(doc);
}
return true;
}
return false;
}
void EditorHelp::_update_theme_item_cache() {
VBoxContainer::_update_theme_item_cache();
@ -705,8 +676,7 @@ void EditorHelp::_pop_code_font() {
}
Error EditorHelp::_goto_desc(const String &p_class) {
// If class doesn't have docs listed, attempt on-demand docgen
if (!doc->class_list.has(p_class) && !_attempt_doc_load(p_class)) {
if (!doc->class_list.has(p_class)) {
return ERR_DOES_NOT_EXIST;
}
@ -1127,7 +1097,7 @@ void EditorHelp::_update_doc() {
class_desc->add_newline();
class_desc->add_newline();
const String &csharp_differences_url = vformat("%s/tutorials/scripting/c_sharp/c_sharp_differences.html", VERSION_DOCS_URL);
const String &csharp_differences_url = vformat("%s/tutorials/scripting/c_sharp/c_sharp_differences.html", GODOT_VERSION_DOCS_URL);
class_desc->push_indent(1);
_push_normal_font();
@ -1704,8 +1674,8 @@ void EditorHelp::_update_doc() {
for (KeyValue<String, Vector<DocData::ConstantDoc>> &E : enums) {
String key = E.key;
if ((key.get_slice_count(".") > 1) && (key.get_slice(".", 0) == edited_class)) {
key = key.get_slice(".", 1);
if ((key.get_slice_count(".") > 1) && (key.get_slicec('.', 0) == edited_class)) {
key = key.get_slicec('.', 1);
}
if (cd.enums.has(key)) {
const bool is_documented = cd.enums[key].is_deprecated || cd.enums[key].is_experimental || !cd.enums[key].description.strip_edges().is_empty();
@ -1906,7 +1876,7 @@ void EditorHelp::_update_doc() {
_push_code_font();
if (constant.value.begins_with("Color(") && constant.value.ends_with(")")) {
String stripped = constant.value.replace(" ", "").replace("Color(", "").replace(")", "");
String stripped = constant.value.remove_char(' ').replace("Color(", "").remove_char(')');
PackedFloat64Array color = stripped.split_floats(",");
if (color.size() >= 3) {
class_desc->push_color(Color(color[0], color[1], color[2]));
@ -2334,11 +2304,11 @@ void EditorHelp::_request_help(const String &p_string) {
}
void EditorHelp::_help_callback(const String &p_topic) {
String what = p_topic.get_slice(":", 0);
String clss = p_topic.get_slice(":", 1);
String what = p_topic.get_slicec(':', 0);
String clss = p_topic.get_slicec(':', 1);
String name;
if (p_topic.get_slice_count(":") == 3) {
name = p_topic.get_slice(":", 2);
name = p_topic.get_slicec(':', 2);
}
_request_help(clss); // First go to class.
@ -2403,12 +2373,10 @@ void EditorHelp::_help_callback(const String &p_topic) {
}
static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const Control *p_owner_node, const String &p_class) {
const DocTools *doc = EditorHelp::get_doc_data();
bool is_native = false;
{
const HashMap<String, DocData::ClassDoc>::ConstIterator E = doc->class_list.find(p_class);
if (E && !E->value.is_script_doc) {
const DocData::ClassDoc *E = EditorHelp::get_doc(p_class);
if (E && !E->is_script_doc) {
is_native = true;
}
}
@ -2439,7 +2407,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C
const Color kbd_bg_color = p_owner_node->get_theme_color(SNAME("kbd_bg_color"), SNAME("EditorHelp"));
const Color param_bg_color = p_owner_node->get_theme_color(SNAME("param_bg_color"), SNAME("EditorHelp"));
String bbcode = p_bbcode.dedent().replace("\t", "").replace("\r", "").strip_edges();
String bbcode = p_bbcode.dedent().remove_chars("\t\r").strip_edges();
// Select the correct code examples.
switch ((int)EDITOR_GET("text_editor/help/class_reference_examples")) {
@ -2517,7 +2485,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C
int brk_end = bbcode.find_char(']', brk_pos + 1);
if (brk_end == -1) {
p_rt->add_text(bbcode.substr(brk_pos, bbcode.length() - brk_pos).replace("\n", "\n\n"));
p_rt->add_text(bbcode.substr(brk_pos).replace("\n", "\n\n"));
break;
}
@ -2622,7 +2590,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C
p_rt->pop(); // font
pos = brk_end + 1;
} else if (doc->class_list.has(tag)) {
} else if (EditorHelp::has_doc(tag)) {
// Use a monospace font for class reference tags such as [Node2D] or [SceneTree].
p_rt->push_font(doc_code_font);
@ -2902,9 +2870,9 @@ void EditorHelp::_add_text(const String &p_bbcode) {
_add_text_to_rt(p_bbcode, class_desc, this, edited_class);
}
void EditorHelp::_wait_for_thread() {
if (worker_thread.is_started()) {
worker_thread.wait_to_finish();
void EditorHelp::_wait_for_thread(Thread &p_thread) {
if (p_thread.is_started()) {
p_thread.wait_to_finish();
}
}
@ -2914,7 +2882,53 @@ void EditorHelp::_compute_doc_version_hash() {
}
String EditorHelp::get_cache_full_path() {
return EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("editor_doc_cache-%d.%d.res", VERSION_MAJOR, VERSION_MINOR));
return EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("editor_doc_cache-%d.%d.res", GODOT_VERSION_MAJOR, GODOT_VERSION_MINOR));
}
String EditorHelp::get_script_doc_cache_full_path() {
return EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_script_doc_cache.res");
}
DocTools *EditorHelp::get_doc_data() {
_wait_for_thread();
return doc;
}
bool EditorHelp::has_doc(const String &p_class_name) {
return get_doc(p_class_name) != nullptr;
}
DocData::ClassDoc *EditorHelp::get_doc(const String &p_class_name) {
return get_doc_data()->class_list.getptr(p_class_name);
}
void EditorHelp::add_doc(const DocData::ClassDoc &p_class_doc) {
if (!_script_docs_loaded.is_set()) {
_docs_to_add.push_back(p_class_doc);
return;
}
get_doc_data()->add_doc(p_class_doc);
}
void EditorHelp::remove_doc(const String &p_class_name) {
if (!_script_docs_loaded.is_set()) {
_docs_to_remove.push_back(p_class_name);
return;
}
DocTools *dt = get_doc_data();
if (dt->has_doc(p_class_name)) {
dt->remove_doc(p_class_name);
}
}
void EditorHelp::remove_script_doc_by_path(const String &p_path) {
if (!_script_docs_loaded.is_set()) {
_docs_to_remove_by_path.push_back(p_path);
return;
}
get_doc_data()->remove_script_doc_by_path(p_path);
}
void EditorHelp::load_xml_buffer(const uint8_t *p_buffer, int p_size) {
@ -2935,23 +2949,26 @@ void EditorHelp::remove_class(const String &p_class) {
}
if (doc && doc->has_doc(p_class)) {
doc->remove_doc(p_class);
remove_doc(p_class);
}
}
void EditorHelp::_load_doc_thread(void *p_udata) {
bool use_script_cache = (bool)p_udata;
Ref<Resource> cache_res = ResourceLoader::load(get_cache_full_path());
if (cache_res.is_valid() && cache_res->get_meta("version_hash", "") == doc_version_hash) {
Array classes = cache_res->get_meta("classes", Array());
for (int i = 0; i < classes.size(); i++) {
doc->add_doc(DocData::ClassDoc::from_dict(classes[i]));
}
if (use_script_cache) {
callable_mp_static(&EditorHelp::load_script_doc_cache).call_deferred();
}
// Extensions' docs are not cached. Generate them now (on the main thread).
callable_mp_static(&EditorHelp::_gen_extensions_docs).call_deferred();
} else {
// We have to go back to the main thread to start from scratch, bypassing any possibly existing cache.
callable_mp_static(&EditorHelp::generate_doc).call_deferred(false);
callable_mp_static(&EditorHelp::generate_doc).call_deferred(false, use_script_cache);
}
OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
@ -2981,6 +2998,12 @@ void EditorHelp::_gen_doc_thread(void *p_udata) {
ERR_PRINT("Cannot save editor help cache (" + get_cache_full_path() + ").");
}
// Load script docs after native ones are cached so native cache doesn't contain script docs.
bool use_script_cache = (bool)p_udata;
if (use_script_cache) {
callable_mp_static(&EditorHelp::load_script_doc_cache).call_deferred();
}
OS::get_singleton()->benchmark_end_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
}
@ -2992,8 +3015,166 @@ void EditorHelp::_gen_extensions_docs() {
doc->merge_from(*ext_doc);
}
}
static void _load_script_doc_cache(bool p_changes) {
EditorHelp::load_script_doc_cache();
}
void EditorHelp::generate_doc(bool p_use_cache) {
void EditorHelp::load_script_doc_cache() {
if (!ProjectSettings::get_singleton()->is_project_loaded()) {
print_verbose("Skipping loading script doc cache since no project is open.");
return;
}
_wait_for_thread();
if (!ResourceLoader::exists(get_script_doc_cache_full_path())) {
print_verbose("Script documentation cache not found. Regenerating it may take a while for projects with many scripts.");
regenerate_script_doc_cache();
return;
}
if (EditorFileSystem::get_singleton()->is_scanning()) {
// This is assuming EditorFileSystem is performing first scan. We must wait until it is done.
EditorFileSystem::get_singleton()->connect(SNAME("sources_changed"), callable_mp_static(_load_script_doc_cache), CONNECT_ONE_SHOT);
return;
}
worker_thread.start(_load_script_doc_cache_thread, nullptr);
}
void EditorHelp::_process_postponed_docs() {
for (const String &class_name : _docs_to_remove) {
doc->remove_doc(class_name);
}
for (const String &path : _docs_to_remove_by_path) {
doc->remove_script_doc_by_path(path);
}
for (const DocData::ClassDoc &cd : _docs_to_add) {
doc->add_doc(cd);
}
_docs_to_add.clear();
_docs_to_remove.clear();
_docs_to_remove_by_path.clear();
}
void EditorHelp::_load_script_doc_cache_thread(void *p_udata) {
ERR_FAIL_COND_MSG(!ProjectSettings::get_singleton()->is_project_loaded(), "Error: cannot load script doc cache without a project.");
ERR_FAIL_COND_MSG(!ResourceLoader::exists(get_script_doc_cache_full_path()), "Error: cannot load script doc cache from inexistent file.");
Ref<Resource> script_doc_cache_res = ResourceLoader::load(get_script_doc_cache_full_path(), "", ResourceFormatLoader::CACHE_MODE_IGNORE);
if (script_doc_cache_res.is_null()) {
print_verbose("Script doc cache is corrupted. Regenerating it instead.");
_delete_script_doc_cache();
callable_mp_static(EditorHelp::regenerate_script_doc_cache).call_deferred();
return;
}
Array classes = script_doc_cache_res->get_meta("classes", Array());
for (const Dictionary dict : classes) {
doc->add_doc(DocData::ClassDoc::from_dict(dict));
}
// Protect from race condition in other threads reading / this thread writing to _docs_to_add/remove/etc.
_script_docs_loaded.set();
// Deal with docs likely added from EditorFileSystem's scans while the cache was loading in EditorHelp::worker_thread.
_process_postponed_docs();
// Always delete the doc cache after successful load since most uses of editor will change a script, invalidating cache.
_delete_script_doc_cache();
}
// Helper method to deal with "sources_changed" signal having a parameter.
static void _regenerate_script_doc_cache(bool p_changes) {
EditorHelp::regenerate_script_doc_cache();
}
void EditorHelp::regenerate_script_doc_cache() {
if (EditorFileSystem::get_singleton()->is_scanning()) {
// Wait until EditorFileSystem scanning is complete to use updated filesystem structure.
EditorFileSystem::get_singleton()->connect(SNAME("sources_changed"), callable_mp_static(_regenerate_script_doc_cache), CONNECT_ONE_SHOT);
return;
}
_wait_for_thread(worker_thread);
_wait_for_thread(loader_thread);
loader_thread.start(_regen_script_doc_thread, EditorFileSystem::get_singleton()->get_filesystem());
}
// Runs on worker_thread since it writes to DocData.
void EditorHelp::_finish_regen_script_doc_thread(void *p_udata) {
loader_thread.wait_to_finish();
_process_postponed_docs();
_script_docs_loaded.set();
OS::get_singleton()->benchmark_end_measure("EditorHelp", "Generate Script Documentation");
}
// Runs on loader_thread since _reload_scripts_documentation calls ResourceLoader::load().
// Avoids deadlocks of worker_thread needing main thread for load task dispatching, but main thread waiting on worker_thread.
void EditorHelp::_regen_script_doc_thread(void *p_udata) {
OS::get_singleton()->benchmark_begin_measure("EditorHelp", "Generate Script Documentation");
EditorFileSystemDirectory *dir = static_cast<EditorFileSystemDirectory *>(p_udata);
_script_docs_loaded.set_to(false);
// Ignore changes from filesystem scan since script docs will be now.
_docs_to_add.clear();
_docs_to_remove.clear();
_docs_to_remove_by_path.clear();
_reload_scripts_documentation(dir);
// All ResourceLoader::load() calls are done, so we can no longer deadlock with main thread.
// Switch to back to worker_thread from loader_thread to resynchronize access to DocData.
worker_thread.start(_finish_regen_script_doc_thread, nullptr);
}
void EditorHelp::_reload_scripts_documentation(EditorFileSystemDirectory *p_dir) {
// Recursively force compile all scripts, which should generate their documentation.
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
_reload_scripts_documentation(p_dir->get_subdir(i));
}
for (int i = 0; i < p_dir->get_file_count(); i++) {
if (ClassDB::is_parent_class(p_dir->get_file_type(i), SNAME("Script"))) {
Ref<Script> scr = ResourceLoader::load(p_dir->get_file_path(i));
if (scr.is_valid()) {
for (const DocData::ClassDoc &cd : scr->get_documentation()) {
_docs_to_add.push_back(cd);
}
}
}
}
}
void EditorHelp::_delete_script_doc_cache() {
if (FileAccess::exists(get_script_doc_cache_full_path())) {
DirAccess::remove_file_or_error(ProjectSettings::get_singleton()->globalize_path(get_script_doc_cache_full_path()));
}
}
void EditorHelp::save_script_doc_cache() {
if (!_script_docs_loaded.is_set()) {
print_verbose("Script docs haven't been properly loaded or regenerated, so don't save them to disk.");
return;
}
Ref<Resource> cache_res;
cache_res.instantiate();
Array classes;
for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
if (E.value.is_script_doc) {
classes.push_back(DocData::ClassDoc::to_dict(E.value));
}
}
cache_res->set_meta("classes", classes);
Error err = ResourceSaver::save(cache_res, get_script_doc_cache_full_path(), ResourceSaver::FLAG_COMPRESS);
ERR_FAIL_COND_MSG(err != OK, vformat("Cannot save script documentation cache in %s.", get_script_doc_cache_full_path()));
}
void EditorHelp::generate_doc(bool p_use_cache, bool p_use_script_cache) {
doc_generation_count++;
OS::get_singleton()->benchmark_begin_measure("EditorHelp", vformat("Generate Documentation (Run %d)", doc_generation_count));
@ -3009,17 +3190,17 @@ void EditorHelp::generate_doc(bool p_use_cache) {
}
if (p_use_cache && FileAccess::exists(get_cache_full_path())) {
worker_thread.start(_load_doc_thread, nullptr);
worker_thread.start(_load_doc_thread, (void *)p_use_script_cache);
} else {
print_verbose("Regenerating editor help cache");
doc->generate();
worker_thread.start(_gen_doc_thread, nullptr);
worker_thread.start(_gen_doc_thread, (void *)p_use_script_cache);
}
}
void EditorHelp::_toggle_scripts_pressed() {
ScriptEditor::get_singleton()->toggle_scripts_panel();
update_toggle_scripts_button();
void EditorHelp::_toggle_files_pressed() {
ScriptEditor::get_singleton()->toggle_files_panel();
update_toggle_files_button();
}
void EditorHelp::_notification(int p_what) {
@ -3053,11 +3234,13 @@ void EditorHelp::_notification(int p_what) {
if (is_inside_tree()) {
_class_desc_resized(true);
}
update_toggle_scripts_button();
update_toggle_files_button();
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_VISIBILITY_CHANGED: {
update_toggle_scripts_button();
update_toggle_files_button();
} break;
}
}
@ -3126,13 +3309,13 @@ void EditorHelp::set_scroll(int p_scroll) {
class_desc->get_v_scroll_bar()->set_value(p_scroll);
}
void EditorHelp::update_toggle_scripts_button() {
void EditorHelp::update_toggle_files_button() {
if (is_layout_rtl()) {
toggle_scripts_button->set_button_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back")));
toggle_files_button->set_button_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_files_panel_toggled() ? SNAME("Forward") : SNAME("Back")));
} else {
toggle_scripts_button->set_button_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward")));
toggle_files_button->set_button_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_files_panel_toggled() ? SNAME("Back") : SNAME("Forward")));
}
toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text()));
toggle_files_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Files Panel"), ED_GET_SHORTCUT("script_editor/toggle_files_panel")->get_as_text()));
}
void EditorHelp::_bind_methods() {
@ -3175,10 +3358,11 @@ EditorHelp::EditorHelp() {
status_bar->set_h_size_flags(SIZE_EXPAND_FILL);
status_bar->set_custom_minimum_size(Size2(0, 24 * EDSCALE));
toggle_scripts_button = memnew(Button);
toggle_scripts_button->set_flat(true);
toggle_scripts_button->connect(SceneStringName(pressed), callable_mp(this, &EditorHelp::_toggle_scripts_pressed));
status_bar->add_child(toggle_scripts_button);
toggle_files_button = memnew(Button);
toggle_files_button->set_accessibility_name(TTRC("Scripts"));
toggle_files_button->set_flat(true);
toggle_files_button->connect(SceneStringName(pressed), callable_mp(this, &EditorHelp::_toggle_files_pressed));
status_bar->add_child(toggle_files_button);
class_desc->set_selection_enabled(true);
class_desc->set_context_menu_enabled(true);
@ -3187,14 +3371,6 @@ EditorHelp::EditorHelp() {
class_desc->hide();
}
EditorHelp::~EditorHelp() {
}
DocTools *EditorHelp::get_doc_data() {
_wait_for_thread();
return doc;
}
/// EditorHelpBit ///
#define HANDLE_DOC(m_string) ((is_native ? DTR(m_string) : (m_string)).strip_edges())
@ -3206,13 +3382,13 @@ EditorHelpBit::HelpData EditorHelpBit::_get_class_help_data(const StringName &p_
HelpData result;
const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native class shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
const String brief_description = HANDLE_DOC(E->value.brief_description);
const String long_description = HANDLE_DOC(E->value.description);
const String brief_description = HANDLE_DOC(class_doc->brief_description);
const String long_description = HANDLE_DOC(class_doc->description);
if (!brief_description.is_empty()) {
result.description += "[b]" + brief_description + "[/b]";
@ -3223,18 +3399,18 @@ EditorHelpBit::HelpData EditorHelpBit::_get_class_help_data(const StringName &p_
}
result.description += long_description;
}
if (E->value.is_deprecated) {
if (E->value.deprecated_message.is_empty()) {
if (class_doc->is_deprecated) {
if (class_doc->deprecated_message.is_empty()) {
result.deprecated_message = TTR("This class may be changed or removed in future versions.");
} else {
result.deprecated_message = HANDLE_DOC(E->value.deprecated_message);
result.deprecated_message = HANDLE_DOC(class_doc->deprecated_message);
}
}
if (E->value.is_experimental) {
if (E->value.experimental_message.is_empty()) {
if (class_doc->is_experimental) {
if (class_doc->experimental_message.is_empty()) {
result.experimental_message = TTR("This class may be changed or removed in future versions.");
} else {
result.experimental_message = HANDLE_DOC(E->value.experimental_message);
result.experimental_message = HANDLE_DOC(class_doc->experimental_message);
}
}
@ -3253,13 +3429,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_enum_help_data(const StringName &p_c
HelpData result;
const DocTools *dd = EditorHelp::get_doc_data();
const HashMap<String, DocData::ClassDoc>::ConstIterator E = dd->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native enums shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const KeyValue<String, DocData::EnumDoc> &kv : E->value.enums) {
for (const KeyValue<String, DocData::EnumDoc> &kv : class_doc->enums) {
const StringName enum_name = kv.key;
const DocData::EnumDoc &enum_doc = kv.value;
@ -3304,13 +3479,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_constant_help_data(const StringName
HelpData result;
const DocTools *dd = EditorHelp::get_doc_data();
const HashMap<String, DocData::ClassDoc>::ConstIterator E = dd->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native constants shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const DocData::ConstantDoc &constant : E->value.constants) {
for (const DocData::ConstantDoc &constant : class_doc->constants) {
HelpData current;
current.description = HANDLE_DOC(constant.description);
if (constant.is_deprecated) {
@ -3356,13 +3530,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_property_help_data(const StringName
HelpData result;
const DocTools *dd = EditorHelp::get_doc_data();
const HashMap<String, DocData::ClassDoc>::ConstIterator E = dd->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native properties shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const DocData::PropertyDoc &property : E->value.properties) {
for (const DocData::PropertyDoc &property : class_doc->properties) {
HelpData current;
current.description = HANDLE_DOC(property.description);
if (property.is_deprecated) {
@ -3397,10 +3570,10 @@ EditorHelpBit::HelpData EditorHelpBit::_get_property_help_data(const StringName
if (!enum_class_name.is_empty() && !enum_name.is_empty()) {
// Classes can use enums from other classes, so check from which it came.
const HashMap<String, DocData::ClassDoc>::ConstIterator enum_class = dd->class_list.find(enum_class_name);
const DocData::ClassDoc *enum_class = EditorHelp::get_doc(enum_class_name);
if (enum_class) {
const String enum_prefix = EditorPropertyNameProcessor::get_singleton()->process_name(enum_name, EditorPropertyNameProcessor::STYLE_CAPITALIZED) + " ";
for (DocData::ConstantDoc constant : enum_class->value.constants) {
for (DocData::ConstantDoc constant : enum_class->constants) {
// Don't display `_MAX` enum value descriptions, as these are never exposed in the inspector.
if (constant.enumeration == enum_name && !constant.name.ends_with("_MAX")) {
// Prettify the enum value display, so that "<ENUM_NAME>_<ITEM>" becomes "Item".
@ -3441,13 +3614,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_theme_item_help_data(const StringNam
HelpData result;
bool found = false;
const DocTools *dd = EditorHelp::get_doc_data();
HashMap<String, DocData::ClassDoc>::ConstIterator E = dd->class_list.find(p_class_name);
while (E) {
DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
while (class_doc) {
// Non-native theme items shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const DocData::ThemeItemDoc &theme_item : E->value.theme_properties) {
for (const DocData::ThemeItemDoc &theme_item : class_doc->theme_properties) {
HelpData current;
current.description = HANDLE_DOC(theme_item.description);
if (theme_item.is_deprecated) {
@ -3481,12 +3653,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_theme_item_help_data(const StringNam
}
}
if (found || E->value.inherits.is_empty()) {
if (found || class_doc->inherits.is_empty()) {
break;
}
// Check for inherited theme items.
E = dd->class_list.find(E->value.inherits);
class_doc = EditorHelp::get_doc(class_doc->inherits);
}
return result;
@ -3499,12 +3671,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_method_help_data(const StringName &p
HelpData result;
const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native methods shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const DocData::MethodDoc &method : E->value.methods) {
for (const DocData::MethodDoc &method : class_doc->methods) {
HelpData current;
current.description = HANDLE_DOC(method.description);
if (method.is_deprecated) {
@ -3552,12 +3724,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_signal_help_data(const StringName &p
HelpData result;
const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native signals shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const DocData::MethodDoc &signal : E->value.signals) {
for (const DocData::MethodDoc &signal : class_doc->signals) {
HelpData current;
current.description = HANDLE_DOC(signal.description);
if (signal.is_deprecated) {
@ -3604,12 +3776,12 @@ EditorHelpBit::HelpData EditorHelpBit::_get_annotation_help_data(const StringNam
HelpData result;
const HashMap<String, DocData::ClassDoc>::ConstIterator E = EditorHelp::get_doc_data()->class_list.find(p_class_name);
if (E) {
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(p_class_name);
if (class_doc) {
// Non-native annotations shouldn't be cached, nor translated.
const bool is_native = !E->value.is_script_doc;
const bool is_native = !class_doc->is_script_doc;
for (const DocData::MethodDoc &annotation : E->value.annotations) {
for (const DocData::MethodDoc &annotation : class_doc->annotations) {
HelpData current;
current.description = HANDLE_DOC(annotation.description);
if (annotation.is_deprecated) {
@ -3699,8 +3871,7 @@ void EditorHelpBit::_update_labels() {
// Nothing to do.
} break;
case SYMBOL_HINT_INHERITANCE: {
const HashMap<String, DocData::ClassDoc> &class_list = EditorHelp::get_doc_data()->class_list;
const DocData::ClassDoc *class_doc = class_list.getptr(symbol_class_name);
const DocData::ClassDoc *class_doc = EditorHelp::get_doc(symbol_class_name);
String inherits = class_doc ? class_doc->inherits : String();
if (!inherits.is_empty()) {
@ -3714,7 +3885,7 @@ void EditorHelpBit::_update_labels() {
_add_type_to_title({ inherits, String(), false });
const DocData::ClassDoc *base_class_doc = class_list.getptr(inherits);
const DocData::ClassDoc *base_class_doc = EditorHelp::get_doc(inherits);
inherits = base_class_doc ? base_class_doc->inherits : String();
}
@ -3989,11 +4160,7 @@ void EditorHelpBit::_meta_clicked(const String &p_select) {
String path = ProjectSettings::get_singleton()->globalize_path(p_select.trim_prefix("open-file:"));
OS::get_singleton()->shell_show_in_file_manager(path, true);
} else if (p_select.begins_with("open-res:")) {
if (help_data.doc_type.type == "PackedScene") {
EditorNode::get_singleton()->load_scene(p_select.trim_prefix("open-res:"));
} else {
EditorNode::get_singleton()->load_resource(p_select.trim_prefix("open-res:"));
}
EditorNode::get_singleton()->load_scene_or_resource(p_select.trim_prefix("open-res:"));
} else if (p_select.begins_with("show:")) {
FileSystemDock::get_singleton()->navigate_to_path(p_select.trim_prefix("show:"));
} else if (p_select.begins_with("http:") || p_select.begins_with("https:")) {
@ -4390,7 +4557,7 @@ void EditorHelpBitTooltip::popup_under_cursor() {
// When `FLAG_POPUP` is false, it prevents the editor from losing focus when displaying the tooltip.
// This way, clicks and double-clicks are still available outside the tooltip.
set_flag(Window::FLAG_POPUP, false);
set_flag(Window::FLAG_NO_FOCUS, true);
set_flag(Window::FLAG_NO_FOCUS, !is_embedded());
popup(r);
}
@ -4410,7 +4577,6 @@ EditorHelpBitTooltip::EditorHelpBitTooltip(Control *p_target) {
set_process_internal(true);
}
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
/// EditorHelpHighlighter ///
EditorHelpHighlighter *EditorHelpHighlighter::singleton = nullptr;
@ -4576,12 +4742,11 @@ EditorHelpHighlighter::~EditorHelpHighlighter() {
#endif
}
#endif // defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
/// FindBar ///
FindBar::FindBar() {
search_text = memnew(LineEdit);
search_text->set_accessibility_name(TTRC("Search help"));
add_child(search_text);
search_text->set_keep_editing_on_text_submit(true);
search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
@ -4594,12 +4759,14 @@ FindBar::FindBar() {
matches_label->hide();
find_prev = memnew(Button);
find_prev->set_accessibility_name(TTRC("Find Previous"));
find_prev->set_flat(true);
add_child(find_prev);
find_prev->set_focus_mode(FOCUS_NONE);
find_prev->connect(SceneStringName(pressed), callable_mp(this, &FindBar::search_prev));
find_next = memnew(Button);
find_next->set_accessibility_name(TTRC("Find Next"));
find_next->set_flat(true);
add_child(find_next);
find_next->set_focus_mode(FOCUS_NONE);
@ -4611,6 +4778,7 @@ FindBar::FindBar() {
hide_button = memnew(TextureButton);
add_child(hide_button);
hide_button->set_accessibility_name(TTRC("Hide"));
hide_button->set_focus_mode(FOCUS_NONE);
hide_button->set_ignore_texture_size(true);
hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_HELP_H
#define EDITOR_HELP_H
#pragma once
#include "core/os/thread.h"
#include "editor/doc_tools.h"
@ -41,8 +40,6 @@
#include "scene/gui/text_edit.h"
#include "scene/main/timer.h"
#include "modules/modules_enabled.gen.h" // For gdscript, mono.
class FindBar : public HBoxContainer {
GDCLASS(FindBar, HBoxContainer);
@ -83,6 +80,8 @@ public:
FindBar();
};
class EditorFileSystemDirectory;
class EditorHelp : public VBoxContainer {
GDCLASS(EditorHelp, VBoxContainer);
@ -112,14 +111,14 @@ class EditorHelp : public VBoxContainer {
RichTextLabel *class_desc = nullptr;
HSplitContainer *h_split = nullptr;
static DocTools *doc;
static DocTools *ext_doc;
inline static DocTools *doc = nullptr;
inline static DocTools *ext_doc = nullptr;
ConfirmationDialog *search_dialog = nullptr;
LineEdit *search = nullptr;
FindBar *find_bar = nullptr;
HBoxContainer *status_bar = nullptr;
Button *toggle_scripts_button = nullptr;
Button *toggle_files_button = nullptr;
String base_path;
@ -186,16 +185,28 @@ class EditorHelp : public VBoxContainer {
void _request_help(const String &p_string);
void _search(bool p_search_previous = false);
void _toggle_scripts_pressed();
void _toggle_files_pressed();
static int doc_generation_count;
static String doc_version_hash;
static Thread worker_thread;
inline static int doc_generation_count = 0;
inline static String doc_version_hash;
inline static Thread worker_thread;
inline static Thread loader_thread; // Only load scripts here to avoid deadlocking with main thread.
static void _wait_for_thread();
inline static SafeFlag _script_docs_loaded = SafeFlag(false);
inline static LocalVector<DocData::ClassDoc> _docs_to_add;
inline static LocalVector<String> _docs_to_remove;
inline static LocalVector<String> _docs_to_remove_by_path;
static void _wait_for_thread(Thread &p_thread = worker_thread);
static void _load_doc_thread(void *p_udata);
static void _gen_doc_thread(void *p_udata);
static void _gen_extensions_docs();
static void _process_postponed_docs();
static void _load_script_doc_cache_thread(void *p_udata);
static void _regen_script_doc_thread(void *p_udata);
static void _finish_regen_script_doc_thread(void *p_udata);
static void _reload_scripts_documentation(EditorFileSystemDirectory *p_dir);
static void _delete_script_doc_cache();
static void _compute_doc_version_hash();
struct PropertyCompare {
@ -215,10 +226,23 @@ protected:
static void _bind_methods();
public:
static void generate_doc(bool p_use_cache = true);
static DocTools *get_doc_data();
static void generate_doc(bool p_use_cache = true, bool p_use_script_cache = true);
static void cleanup_doc();
static void load_script_doc_cache();
static void regenerate_script_doc_cache();
static void save_script_doc_cache();
static String get_cache_full_path();
static String get_script_doc_cache_full_path();
// Adding scripts to DocData directly may make script doc cache inconsistent. Use methods below when adding script docs.
// Usage during startup can also cause deadlocks.
static DocTools *get_doc_data();
// Method forwarding to underlying DocTools to keep script doc cache consistent.
static DocData::ClassDoc *get_doc(const String &p_class_name);
static void add_doc(const DocData::ClassDoc &p_class_doc);
static void remove_doc(const String &p_class_name);
static void remove_script_doc_by_path(const String &p_path);
static bool has_doc(const String &p_class_name);
static void load_xml_buffer(const uint8_t *p_buffer, int p_size);
static void remove_class(const String &p_class);
@ -240,12 +264,11 @@ public:
int get_scroll() const;
void set_scroll(int p_scroll);
void update_toggle_scripts_button();
void update_toggle_files_button();
static void init_gdext_pointers();
EditorHelp();
~EditorHelp();
};
class EditorHelpBit : public VBoxContainer {
@ -361,7 +384,6 @@ public:
EditorHelpBitTooltip(Control *p_target);
};
#if defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
class EditorSyntaxHighlighter;
class EditorHelpHighlighter {
@ -396,6 +418,3 @@ public:
EditorHelpHighlighter();
virtual ~EditorHelpHighlighter();
};
#endif // defined(MODULE_GDSCRIPT_ENABLED) || defined(MODULE_MONO_ENABLED)
#endif // EDITOR_HELP_H

View file

@ -328,6 +328,7 @@ EditorHelpSearch::EditorHelpSearch() {
vbox->add_child(hbox);
search_box = memnew(LineEdit);
search_box->set_accessibility_name(TTRC("Search"));
search_box->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->set_clear_button_enabled(true);
@ -339,6 +340,7 @@ EditorHelpSearch::EditorHelpSearch() {
case_sensitive_button = memnew(Button);
case_sensitive_button->set_theme_type_variation(SceneStringName(FlatButton));
case_sensitive_button->set_tooltip_text(TTR("Case Sensitive"));
case_sensitive_button->set_accessibility_name(TTRC("Case Sensitive"));
case_sensitive_button->connect(SceneStringName(pressed), callable_mp(this, &EditorHelpSearch::_update_results));
case_sensitive_button->set_toggle_mode(true);
case_sensitive_button->set_focus_mode(Control::FOCUS_NONE);
@ -347,6 +349,7 @@ EditorHelpSearch::EditorHelpSearch() {
hierarchy_button = memnew(Button);
hierarchy_button->set_theme_type_variation(SceneStringName(FlatButton));
hierarchy_button->set_tooltip_text(TTR("Show Hierarchy"));
hierarchy_button->set_accessibility_name(TTRC("Show Hierarchy"));
hierarchy_button->connect(SceneStringName(pressed), callable_mp(this, &EditorHelpSearch::_update_results));
hierarchy_button->set_toggle_mode(true);
hierarchy_button->set_pressed(true);
@ -354,6 +357,7 @@ EditorHelpSearch::EditorHelpSearch() {
hbox->add_child(hierarchy_button);
filter_combo = memnew(OptionButton);
filter_combo->set_accessibility_name(TTRC("Filter"));
filter_combo->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
filter_combo->set_stretch_ratio(0); // Fixed width.
filter_combo->add_item(TTR("Display All"), SEARCH_ALL);
@ -372,6 +376,7 @@ EditorHelpSearch::EditorHelpSearch() {
// Create the results tree.
results_tree = memnew(Tree);
results_tree->set_accessibility_name(TTRC("Search Results"));
results_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
results_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
results_tree->set_columns(2);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_HELP_SEARCH_H
#define EDITOR_HELP_SEARCH_H
#pragma once
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/dialogs.h"
@ -212,5 +211,3 @@ public:
Runner(Control *p_icon_service, Tree *p_results_tree, TreeCache *p_tree_cache, const String &p_term, int p_search_flags);
};
#endif // EDITOR_HELP_SEARCH_H

View file

@ -33,6 +33,7 @@
#include "core/os/keyboard.h"
#include "editor/add_metadata_dialog.h"
#include "editor/debugger/editor_debugger_inspector.h"
#include "editor/doc_tools.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_main_screen.h"
@ -252,6 +253,25 @@ void EditorProperty::emit_changed(const StringName &p_property, const Variant &p
void EditorProperty::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
DisplayServer::get_singleton()->accessibility_update_set_name(ae, vformat(TTR("Property: %s"), label));
DisplayServer::get_singleton()->accessibility_update_set_value(ae, vformat(TTR("Property: %s"), label));
DisplayServer::get_singleton()->accessibility_update_set_popup_type(ae, DisplayServer::AccessibilityPopupType::POPUP_MENU);
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_CONTEXT_MENU, callable_mp(this, &EditorProperty::_accessibility_action_menu));
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_CLICK, callable_mp(this, &EditorProperty::_accessibility_action_click));
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_READONLY, read_only);
if (checkable) {
DisplayServer::get_singleton()->accessibility_update_set_checked(ae, checked);
}
} break;
case NOTIFICATION_SORT_CHILDREN: {
Size2 size = get_size();
Rect2 rect;
@ -578,6 +598,12 @@ void EditorProperty::_notification(int p_what) {
}
} break;
case NOTIFICATION_ENTER_TREE: {
EditorInspector *inspector = get_parent_inspector();
if (inspector) {
inspector = inspector->get_root_inspector();
}
set_shortcut_context(inspector);
if (has_borders) {
get_parent()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorProperty::_update_property_bg));
_update_property_bg();
@ -617,6 +643,16 @@ StringName EditorProperty::get_edited_property() const {
return property;
}
Variant EditorProperty::get_edited_property_display_value() const {
ERR_FAIL_NULL_V(object, Variant());
Control *control = Object::cast_to<Control>(object);
if (checkable && !checked && control && String(property).begins_with("theme_override_")) {
return control->get_used_theme_item(property);
} else {
return get_edited_property_value();
}
}
EditorInspector *EditorProperty::get_parent_inspector() const {
Node *parent = get_parent();
while (parent) {
@ -769,9 +805,7 @@ bool EditorProperty::use_keying_next() const {
List<PropertyInfo> plist;
object->get_property_list(&plist, true);
for (List<PropertyInfo>::Element *I = plist.front(); I; I = I->next()) {
PropertyInfo &p = I->get();
for (const PropertyInfo &p : plist) {
if (p.name == property) {
return (p.usage & PROPERTY_USAGE_KEYING_INCREMENTS);
}
@ -918,6 +952,9 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
if (me.is_valid()) {
Vector2 mpos = me->get_position();
if (bottom_child_rect.has_point(mpos)) {
return; // Makes child EditorProperties behave like sibling nodes when handling mouse events.
}
if (is_layout_rtl()) {
mpos.x = get_size().x - mpos.x;
}
@ -1013,6 +1050,22 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
}
}
void EditorProperty::_accessibility_action_click(const Variant &p_data) {
select();
if (checkable) {
checked = !checked;
queue_redraw();
emit_signal(SNAME("property_checked"), property, checked);
}
}
void EditorProperty::_accessibility_action_menu(const Variant &p_data) {
_update_popup();
menu->set_position(get_screen_position());
menu->reset_size();
menu->popup();
}
void EditorProperty::shortcut_input(const Ref<InputEvent> &p_event) {
if (!selected || !p_event->is_pressed()) {
return;
@ -1250,6 +1303,19 @@ void EditorProperty::menu_option(int p_option) {
emit_signal(SNAME("property_pinned"), property, !pinned);
queue_redraw();
} break;
case MENU_DELETE: {
accept_event();
emit_signal(SNAME("property_deleted"), property);
} break;
case MENU_REVERT_VALUE: {
accept_event();
get_viewport()->gui_release_focus();
bool is_valid_revert = false;
Variant revert_value = EditorPropertyRevert::get_property_revert_value(object, property, &is_valid_revert);
ERR_FAIL_COND(!is_valid_revert);
emit_changed(_get_revert_property(), revert_value);
update_property();
} break;
case MENU_OPEN_DOCUMENTATION: {
ScriptEditor::get_singleton()->goto_help(doc_path);
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
@ -1343,6 +1409,8 @@ void EditorProperty::_bind_methods() {
}
EditorProperty::EditorProperty() {
set_focus_mode(FOCUS_ACCESSIBILITY);
object = nullptr;
split_ratio = 0.5;
text_size = 0;
@ -1377,6 +1445,7 @@ void EditorProperty::_update_popup() {
menu->add_icon_item(get_editor_theme_icon(SNAME("Unfavorite")), TTR("Unfavorite Property"), MENU_FAVORITE_PROPERTY);
menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be put back at its original place."));
} else {
// TRANSLATORS: This is a menu item to add a property to the favorites.
menu->add_icon_item(get_editor_theme_icon(SNAME("Favorites")), TTR("Favorite Property"), MENU_FAVORITE_PROPERTY);
menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be placed at the top for all objects of this class."));
}
@ -1392,7 +1461,15 @@ void EditorProperty::_update_popup() {
}
menu->set_item_tooltip(menu->get_item_index(MENU_PIN_VALUE), TTR("Pinning a value forces it to be saved even if it's equal to the default."));
}
if (deletable || can_revert) {
menu->add_separator();
if (deletable) {
menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete Property"), MENU_PIN_VALUE);
}
if (can_revert) {
menu->add_icon_item(get_editor_theme_icon(SNAME("Reload")), TTR("Revert Value"), MENU_REVERT_VALUE);
}
}
if (!doc_path.is_empty()) {
menu->add_separator();
menu->add_icon_item(get_editor_theme_icon(SNAME("Help")), TTR("Open Documentation"), MENU_OPEN_DOCUMENTATION);
@ -1471,6 +1548,19 @@ void EditorInspectorPlugin::_bind_methods() {
void EditorInspectorCategory::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
DisplayServer::get_singleton()->accessibility_update_set_name(ae, vformat(TTR("Category: %s"), label));
DisplayServer::get_singleton()->accessibility_update_set_value(ae, vformat(TTR("Category: %s"), label));
DisplayServer::get_singleton()->accessibility_update_set_popup_type(ae, DisplayServer::AccessibilityPopupType::POPUP_MENU);
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_CONTEXT_MENU, callable_mp(this, &EditorInspectorCategory::_accessibility_action_menu));
} break;
case NOTIFICATION_THEME_CHANGED: {
if (menu) {
if (is_favorite) {
@ -1524,6 +1614,22 @@ void EditorInspectorCategory::_notification(int p_what) {
}
}
void EditorInspectorCategory::_accessibility_action_menu(const Variant &p_data) {
if (!is_favorite) {
if (!menu) {
menu = memnew(PopupMenu);
menu->add_icon_item(get_editor_theme_icon(SNAME("Help")), TTR("Open Documentation"), MENU_OPEN_DOCS);
add_child(menu);
menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option));
}
menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name));
}
menu->set_position(get_screen_position());
menu->reset_size();
menu->popup();
}
Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const {
// If it's not a doc tooltip, fallback to the default one.
if (doc_class_name.is_empty()) {
@ -1594,6 +1700,10 @@ void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) {
menu->popup();
}
EditorInspectorCategory::EditorInspectorCategory() {
set_focus_mode(FOCUS_ACCESSIBILITY);
}
////////////////////////////////////////////////
////////////////////////////////////////////////
@ -1637,10 +1747,24 @@ int EditorInspectorSection::_get_header_height() {
void EditorInspectorSection::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
DisplayServer::get_singleton()->accessibility_update_set_name(ae, vformat(TTR("Section: %s"), label));
DisplayServer::get_singleton()->accessibility_update_set_value(ae, vformat(TTR("Section: %s"), label));
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_COLLAPSE, callable_mp(this, &EditorInspectorSection::_accessibility_action_collapse));
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_EXPAND, callable_mp(this, &EditorInspectorSection::_accessibility_action_expand));
} break;
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
bg_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
bg_color.a /= level;
int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"));
vbox->add_theme_constant_override(SNAME("separation"), separation);
} break;
case NOTIFICATION_SORT_CHILDREN: {
@ -1864,6 +1988,20 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) {
return;
}
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed()) {
if (k->is_action("ui_accept", true)) {
accept_event();
bool should_unfold = !object->editor_is_section_unfolded(section);
if (should_unfold) {
unfold();
} else {
fold();
}
}
}
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (object->editor_is_section_unfolded(section)) {
@ -1885,6 +2023,15 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) {
} else if (mb.is_valid() && !mb->is_pressed()) {
queue_redraw();
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
int header_height = _get_header_height();
Vector2 previous = mm->get_position() - mm->get_relative();
if ((mm->get_position().y >= header_height) != (previous.y >= header_height)) {
queue_redraw();
}
}
}
String EditorInspectorSection::get_section() const {
@ -1895,6 +2042,14 @@ VBoxContainer *EditorInspectorSection::get_vbox() {
return vbox;
}
void EditorInspectorSection::_accessibility_action_collapse(const Variant &p_data) {
fold();
}
void EditorInspectorSection::_accessibility_action_expand(const Variant &p_data) {
unfold();
}
void EditorInspectorSection::unfold() {
if (!foldable) {
return;
@ -1926,6 +2081,12 @@ void EditorInspectorSection::set_bg_color(const Color &p_bg_color) {
queue_redraw();
}
void EditorInspectorSection::reset_timer() {
if (dropping_for_unfold && !dropping_unfold_timer->is_stopped()) {
dropping_unfold_timer->start();
}
}
bool EditorInspectorSection::has_revertable_properties() const {
return !revertable_properties.is_empty();
}
@ -1950,6 +2111,8 @@ void EditorInspectorSection::_bind_methods() {
}
EditorInspectorSection::EditorInspectorSection() {
set_focus_mode(FOCUS_ACCESSIBILITY);
vbox = memnew(VBoxContainer);
dropping_unfold_timer = memnew(Timer);
@ -2063,6 +2226,18 @@ void EditorInspectorArray::_panel_draw(int p_index) {
}
}
void EditorInspectorArray::_panel_gui_focus(int p_index) {
array_elements[p_index].panel->queue_redraw();
selected = p_index;
}
void EditorInspectorArray::_panel_gui_unfocus(int p_index) {
array_elements[p_index].panel->queue_redraw();
if (selected == p_index) {
selected = -1;
}
}
void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index) {
ERR_FAIL_INDEX(p_index, (int)array_elements.size());
@ -2087,13 +2262,22 @@ void EditorInspectorArray::_panel_gui_input(Ref<InputEvent> p_event, int p_index
popup_array_index_pressed = begin_array_index + p_index;
rmb_popup->set_item_disabled(OPTION_MOVE_UP, popup_array_index_pressed == 0);
rmb_popup->set_item_disabled(OPTION_MOVE_DOWN, popup_array_index_pressed == count - 1);
rmb_popup->set_position(get_screen_position() + mb->get_position());
rmb_popup->set_position(array_elements[p_index].panel->get_screen_position() + mb->get_position());
rmb_popup->reset_size();
rmb_popup->popup();
}
}
}
void EditorInspectorArray::show_menu(int p_index, const Vector2 &p_offset) {
popup_array_index_pressed = begin_array_index + p_index;
rmb_popup->set_item_disabled(OPTION_MOVE_UP, popup_array_index_pressed == 0);
rmb_popup->set_item_disabled(OPTION_MOVE_DOWN, popup_array_index_pressed == count - 1);
rmb_popup->set_position(get_screen_position() + p_offset);
rmb_popup->reset_size();
rmb_popup->popup();
}
void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
String action_name;
if (p_element_index < 0) {
@ -2193,10 +2377,8 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
undo_redo->add_undo_property(object, count_property, properties_as_array.size());
for (int i = 0; i < (int)properties_as_array.size(); i++) {
Dictionary d = Dictionary(properties_as_array[i]);
Array keys = d.keys();
for (int j = 0; j < keys.size(); j++) {
String key = keys[j];
undo_redo->add_undo_property(object, vformat(key, i), d[key]);
for (const KeyValue<Variant, Variant> &kv : d) {
undo_redo->add_undo_property(object, vformat(kv.key, i), kv.value);
}
}
@ -2216,10 +2398,8 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
undo_redo->add_do_property(object, count_property, properties_as_array.size());
for (int i = 0; i < (int)properties_as_array.size(); i++) {
Dictionary d = properties_as_array[i];
Array keys = d.keys();
for (int j = 0; j < keys.size(); j++) {
String key = keys[j];
undo_redo->add_do_property(object, vformat(key, i), d[key]);
for (const KeyValue<Variant, Variant> &kv : d) {
undo_redo->add_do_property(object, vformat(kv.key, i), kv.value);
}
}
}
@ -2270,10 +2450,8 @@ void EditorInspectorArray::_clear_array() {
undo_redo->add_undo_property(object, count_property, count);
for (int i = 0; i < (int)properties_as_array.size(); i++) {
Dictionary d = Dictionary(properties_as_array[i]);
Array keys = d.keys();
for (int j = 0; j < keys.size(); j++) {
String key = keys[j];
undo_redo->add_undo_property(object, vformat(key, i), d[key]);
for (const KeyValue<Variant, Variant> &kv : d) {
undo_redo->add_undo_property(object, vformat(kv.key, i), kv.value);
}
}
@ -2335,10 +2513,8 @@ void EditorInspectorArray::_resize_array(int p_size) {
undo_redo->add_undo_property(object, count_property, count);
for (int i = count - 1; i > p_size - 1; i--) {
Dictionary d = Dictionary(properties_as_array[i]);
Array keys = d.keys();
for (int j = 0; j < keys.size(); j++) {
String key = keys[j];
undo_redo->add_undo_property(object, vformat(key, i), d[key]);
for (const KeyValue<Variant, Variant> &kv : d) {
undo_redo->add_undo_property(object, vformat(kv.key, i), kv.value);
}
}
@ -2462,16 +2638,20 @@ void EditorInspectorArray::_setup() {
ArrayElement &ae = array_elements[i];
// Panel and its hbox.
ae.panel = memnew(PanelContainer);
ae.panel = memnew(ArrayPanelContainer);
ae.panel->set_focus_mode(FOCUS_ALL);
ae.panel->set_mouse_filter(MOUSE_FILTER_PASS);
SET_DRAG_FORWARDING_GCD(ae.panel, EditorInspectorArray);
int element_position = begin_array_index + i;
String ae_name = vformat(TTR("Element %d: %s%d*"), element_position, array_element_prefix, element_position);
ae.panel->set_meta("index", element_position);
ae.panel->set_tooltip_text(vformat(TTR("Element %d: %s%d*"), element_position, array_element_prefix, element_position));
ae.panel->connect(SceneStringName(focus_entered), callable_mp((CanvasItem *)ae.panel, &PanelContainer::queue_redraw));
ae.panel->connect(SceneStringName(focus_exited), callable_mp((CanvasItem *)ae.panel, &PanelContainer::queue_redraw));
ae.panel->set_meta("name", ae_name);
ae.panel->set_meta("element", this);
ae.panel->set_tooltip_text(ae_name);
ae.panel->connect(SceneStringName(focus_entered), callable_mp(this, &EditorInspectorArray::_panel_gui_focus).bind(i));
ae.panel->connect(SceneStringName(focus_exited), callable_mp(this, &EditorInspectorArray::_panel_gui_unfocus).bind(i));
ae.panel->connect(SceneStringName(draw), callable_mp(this, &EditorInspectorArray::_panel_draw).bind(i));
ae.panel->connect(SceneStringName(gui_input), callable_mp(this, &EditorInspectorArray::_panel_gui_input).bind(i));
ae.panel->add_theme_style_override(SceneStringName(panel), i % 2 ? odd_style : even_style);
@ -2504,6 +2684,7 @@ void EditorInspectorArray::_setup() {
if (element_position > 0) {
ae.move_up = memnew(Button);
ae.move_up->set_accessibility_name(TTRC("Move Up"));
ae.move_up->set_button_icon(get_editor_theme_icon(SNAME("MoveUp")));
ae.move_up->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position - 1));
move_vbox->add_child(ae.move_up);
@ -2520,6 +2701,7 @@ void EditorInspectorArray::_setup() {
if (element_position < count - 1) {
ae.move_down = memnew(Button);
ae.move_down->set_accessibility_name(TTRC("Move Down"));
ae.move_down->set_button_icon(get_editor_theme_icon(SNAME("MoveDown")));
ae.move_down->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position + 2));
move_vbox->add_child(ae.move_down);
@ -2544,6 +2726,7 @@ void EditorInspectorArray::_setup() {
if (!unresizable) {
ae.erase = memnew(Button);
ae.erase->set_accessibility_name(TTRC("Remove"));
ae.erase->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
ae.erase->set_v_size_flags(SIZE_SHRINK_CENTER);
ae.erase->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_remove_item).bind(element_position));
@ -2584,7 +2767,7 @@ void EditorInspectorArray::drop_data_fw(const Point2 &p_point, const Variant &p_
Dictionary dict = p_data;
int to_drop = dict["index"];
int drop_position = _drop_position();
int drop_position = (p_point == Vector2(Math::INF, Math::INF)) ? selected : _drop_position();
if (drop_position < 0) {
return;
}
@ -2602,7 +2785,7 @@ bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant
return false;
}
Dictionary dict = p_data;
int drop_position = _drop_position();
int drop_position = (p_point == Vector2(Math::INF, Math::INF)) ? selected : _drop_position();
if (!dict.has("type") || dict["type"] != "property_array_element" || String(dict["property_array_prefix"]) != array_element_prefix || drop_position < 0) {
return false;
}
@ -2614,8 +2797,45 @@ bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant
return drop_array_index != moved_array_index && drop_array_index - 1 != moved_array_index;
}
void ArrayPanelContainer::_accessibility_action_menu(const Variant &p_data) {
EditorInspectorArray *el = Object::cast_to<EditorInspectorArray>(get_meta("element"));
if (el) {
int index = get_meta("index");
el->show_menu(index, Vector2());
}
}
void ArrayPanelContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
DisplayServer::get_singleton()->accessibility_update_set_name(ae, get_meta("text"));
DisplayServer::get_singleton()->accessibility_update_set_value(ae, get_meta("text"));
DisplayServer::get_singleton()->accessibility_update_set_popup_type(ae, DisplayServer::AccessibilityPopupType::POPUP_MENU);
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_CONTEXT_MENU, callable_mp(this, &ArrayPanelContainer::_accessibility_action_menu));
} break;
}
}
ArrayPanelContainer::ArrayPanelContainer() {
set_focus_mode(FOCUS_ACCESSIBILITY);
}
void EditorInspectorArray::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
DisplayServer::get_singleton()->accessibility_update_set_name(ae, vformat(TTR("Array: %s"), get_label()));
DisplayServer::get_singleton()->accessibility_update_set_value(ae, vformat(TTR("Array: %s"), get_label()));
} break;
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
Color color = get_theme_color(SNAME("dark_color_1"), EditorStringName(Editor));
@ -2715,12 +2935,11 @@ VBoxContainer *EditorInspectorArray::get_vbox(int p_index) {
EditorInspectorArray::EditorInspectorArray(bool p_read_only) {
read_only = p_read_only;
set_mouse_filter(Control::MOUSE_FILTER_STOP);
odd_style.instantiate();
even_style.instantiate();
rmb_popup = memnew(PopupMenu);
rmb_popup->set_accessibility_name(TTRC("Move"));
rmb_popup->add_item(TTR("Move Up"), OPTION_MOVE_UP);
rmb_popup->add_item(TTR("Move Down"), OPTION_MOVE_DOWN);
rmb_popup->add_separator();
@ -2758,6 +2977,7 @@ EditorInspectorArray::EditorInspectorArray(bool p_read_only) {
resize_dialog->add_child(resize_dialog_vbox);
new_size_spin_box = memnew(SpinBox);
new_size_spin_box->set_accessibility_name(TTRC("Size"));
new_size_spin_box->set_max(16384);
new_size_spin_box->connect(SceneStringName(value_changed), callable_mp(this, &EditorInspectorArray::_new_size_spin_box_value_changed));
new_size_spin_box->get_line_edit()->connect(SceneStringName(text_submitted), callable_mp(this, &EditorInspectorArray::_new_size_spin_box_text_submitted));
@ -2833,16 +3053,19 @@ EditorPaginator::EditorPaginator() {
set_alignment(ALIGNMENT_CENTER);
first_page_button = memnew(Button);
first_page_button->set_accessibility_name(TTRC("First Page"));
first_page_button->set_flat(true);
first_page_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPaginator::_first_page_button_pressed));
add_child(first_page_button);
prev_page_button = memnew(Button);
prev_page_button->set_accessibility_name(TTRC("Previuos Page"));
prev_page_button->set_flat(true);
prev_page_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPaginator::_prev_page_button_pressed));
add_child(prev_page_button);
page_line_edit = memnew(LineEdit);
page_line_edit->set_accessibility_name(TTRC("Page"));
page_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPaginator::_page_line_edit_text_submitted));
page_line_edit->add_theme_constant_override("minimum_character_width", 2);
add_child(page_line_edit);
@ -2851,11 +3074,13 @@ EditorPaginator::EditorPaginator() {
add_child(page_count_label);
next_page_button = memnew(Button);
prev_page_button->set_accessibility_name(TTRC("Next Page"));
next_page_button->set_flat(true);
next_page_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPaginator::_next_page_button_pressed));
add_child(next_page_button);
last_page_button = memnew(Button);
last_page_button->set_accessibility_name(TTRC("Last Page"));
last_page_button->set_flat(true);
last_page_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPaginator::_last_page_button_pressed));
add_child(last_page_button);
@ -2968,7 +3193,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn
ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned));
ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected));
ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed));
ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), CONNECT_DEFERRED);
ep->connect("resource_selected", callable_mp(get_root_inspector(), &EditorInspector::_resource_selected), CONNECT_DEFERRED);
ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), CONNECT_DEFERRED);
if (F.properties.size()) {
@ -3040,15 +3265,21 @@ bool EditorInspector::_is_property_disabled_by_feature_profile(const StringName
}
void EditorInspector::update_tree() {
if (!object) {
return;
}
bool root_inspector_was_following_focus = get_root_inspector()->is_following_focus();
if (root_inspector_was_following_focus) {
// Temporarily disable focus following on the root inspector to avoid jumping while the inspector is updating.
get_root_inspector()->set_follow_focus(false);
}
// Store currently selected and focused elements to restore after the update.
// TODO: Can be useful to store more context for the focusable, such as the caret position in LineEdit.
StringName current_selected = property_selected;
int current_focusable = -1;
// Temporarily disable focus following on the root inspector to avoid jumping while the inspector is updating.
bool was_following = get_root_inspector()->is_following_focus();
get_root_inspector()->set_follow_focus(false);
if (property_focusable != -1) {
// Check that focusable is actually focusable.
bool restore_focus = false;
@ -3070,14 +3301,9 @@ void EditorInspector::update_tree() {
}
}
// Only hide plugins if we are not editing any object.
// This should be handled outside of the update_tree call anyway (see EditorInspector::edit), but might as well keep it safe.
_clear(!object);
if (!object) {
get_root_inspector()->set_follow_focus(was_following);
return;
}
// The call here is for the edited object that has not changed, but the tree needs to be updated (for example, the object's property list has been modified).
// Since the edited object has not changed, there is no need to hide the plugin at this time.
_clear(false);
List<Ref<EditorInspectorPlugin>> valid_plugins;
@ -3132,13 +3358,14 @@ void EditorInspector::update_tree() {
Color sscolor = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
bool sub_inspectors_enabled = EDITOR_GET("interface/inspector/open_resources_in_current_inspector");
// Get the lists of editors to add the beginning.
for (Ref<EditorInspectorPlugin> &ped : valid_plugins) {
ped->parse_begin(object);
_parse_added_editors(begin_vbox, nullptr, ped);
}
if (begin_vbox->get_child_count()) {
if (!valid_plugins.is_empty()) {
begin_vbox->show();
// Get the lists of editors to add the beginning.
for (Ref<EditorInspectorPlugin> &ped : valid_plugins) {
ped->parse_begin(object);
_parse_added_editors(begin_vbox, nullptr, ped);
}
}
StringName doc_name;
@ -3440,6 +3667,8 @@ void EditorInspector::update_tree() {
// Recreate the category vbox if it was reset.
if (category_vbox == nullptr) {
category_vbox = memnew(VBoxContainer);
int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"));
category_vbox->add_theme_constant_override(SNAME("separation"), separation);
category_vbox->hide();
main_vbox->add_child(category_vbox);
}
@ -3467,6 +3696,7 @@ void EditorInspector::update_tree() {
if (!vbox_per_path[root_vbox].has(acc_path)) {
// If the section does not exists, create it.
EditorInspectorSection *section = memnew(EditorInspectorSection);
get_root_inspector()->get_v_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(section, &EditorInspectorSection::reset_timer).unbind(1));
current_vbox->add_child(section);
sections.push_back(section);
@ -3535,9 +3765,9 @@ void EditorInspector::update_tree() {
String swap_method;
for (int i = (p.type == Variant::NIL ? 1 : 2); i < class_name_components.size(); i++) {
if (class_name_components[i].begins_with("page_size") && class_name_components[i].get_slice_count("=") == 2) {
page_size = class_name_components[i].get_slice("=", 1).to_int();
page_size = class_name_components[i].get_slicec('=', 1).to_int();
} else if (class_name_components[i].begins_with("add_button_text") && class_name_components[i].get_slice_count("=") == 2) {
add_button_text = class_name_components[i].get_slice("=", 1).strip_edges();
add_button_text = class_name_components[i].get_slicec('=', 1).strip_edges();
} else if (class_name_components[i] == "static") {
movable = false;
} else if (class_name_components[i] == "const") {
@ -3547,7 +3777,7 @@ void EditorInspector::update_tree() {
} else if (class_name_components[i] == "unfoldable") {
foldable = false;
} else if (class_name_components[i].begins_with("swap_method") && class_name_components[i].get_slice_count("=") == 2) {
swap_method = class_name_components[i].get_slice("=", 1).strip_edges();
swap_method = class_name_components[i].get_slicec('=', 1).strip_edges();
}
}
@ -3807,7 +4037,7 @@ void EditorInspector::update_tree() {
ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned));
ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected));
ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed));
ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), CONNECT_DEFERRED);
ep->connect("resource_selected", callable_mp(get_root_inspector(), &EditorInspector::_resource_selected), CONNECT_DEFERRED);
ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), CONNECT_DEFERRED);
if (use_doc_hints) {
@ -3867,6 +4097,7 @@ void EditorInspector::update_tree() {
}
EditorInspectorSection *section = memnew(EditorInspectorSection);
get_root_inspector()->get_v_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(section, &EditorInspectorSection::reset_timer).unbind(1));
favorites_groups_vbox->add_child(section);
parent_vbox = section->get_vbox();
section->setup("", section_name, object, sscolor, false);
@ -3886,6 +4117,7 @@ void EditorInspector::update_tree() {
}
EditorInspectorSection *section = memnew(EditorInspectorSection);
get_root_inspector()->get_v_scroll_bar()->connect(SceneStringName(value_changed), callable_mp(section, &EditorInspectorSection::reset_timer).unbind(1));
vbox->add_child(section);
vbox = section->get_vbox();
section->setup("", section_name, object, sscolor, false);
@ -3918,8 +4150,9 @@ void EditorInspector::update_tree() {
}
// Clean up empty sections.
for (List<EditorInspectorSection *>::Element *I = sections.back(); I; I = I->prev()) {
for (List<EditorInspectorSection *>::Element *I = sections.back(); I;) {
EditorInspectorSection *section = I->get();
I = I->prev(); // Note: Advance before erasing element.
if (section->get_vbox()->get_child_count() == 0) {
sections.erase(section);
vbox_per_path[main_vbox].erase(section->get_section());
@ -3954,7 +4187,9 @@ void EditorInspector::update_tree() {
EditorNode::get_singleton()->hide_unused_editors();
}
get_root_inspector()->set_follow_focus(was_following);
if (root_inspector_was_following_focus) {
get_root_inspector()->set_follow_focus(true);
}
}
void EditorInspector::update_property(const String &p_prop) {
@ -4271,6 +4506,10 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo
Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field);
_edit_request_change(object, p_name);
emit_signal(_prop_edited, p_name);
} else if (Object::cast_to<EditorDebuggerRemoteObjects>(object)) {
Object::cast_to<EditorDebuggerRemoteObjects>(object)->set_property_field(p_name, p_value, p_changed_field);
_edit_request_change(object, p_name);
emit_signal(_prop_edited, p_name);
} else {
undo_redo->create_action(vformat(TTR("Set %s"), p_name), UndoRedo::MERGE_ENDS);
undo_redo->add_do_property(object, p_name, p_value);
@ -4450,13 +4689,26 @@ void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
_edit_set(p_path, Variant(), false, "");
} else {
Variant to_create;
List<PropertyInfo> pinfo;
object->get_property_list(&pinfo);
for (const PropertyInfo &E : pinfo) {
if (E.name == p_path) {
Callable::CallError ce;
Variant::construct(E.type, to_create, nullptr, 0, ce);
break;
Control *control = Object::cast_to<Control>(object);
bool skip = false;
if (control && p_path.begins_with("theme_override_")) {
to_create = control->get_used_theme_item(p_path);
Ref<Resource> resource = to_create;
if (resource.is_valid()) {
to_create = resource->duplicate();
}
skip = true;
}
if (!skip) {
List<PropertyInfo> pinfo;
object->get_property_list(&pinfo);
for (const PropertyInfo &E : pinfo) {
if (E.name == p_path) {
Callable::CallError ce;
Variant::construct(E.type, to_create, nullptr, 0, ce);
break;
}
}
}
_edit_set(p_path, to_create, false, "");
@ -4528,14 +4780,6 @@ void EditorInspector::_node_removed(Node *p_node) {
}
}
void EditorInspector::_gui_focus_changed(Control *p_control) {
if (!is_visible_in_tree() && !is_following_focus()) {
return;
}
// Don't follow focus when the inspector nor any of its children is focused. Prevents potential jumping when gaining focus.
set_follow_focus(has_focus() || child_has_focus());
}
void EditorInspector::_update_current_favorites() {
current_favorites.clear();
if (!can_favorite) {
@ -4608,7 +4852,7 @@ void EditorInspector::_set_property_favorited(const String &p_path, bool p_favor
String theme_property;
if (p_path.begins_with("theme_override_")) {
theme_property = p_path.get_slice("/", 1);
theme_property = p_path.get_slicec('/', 1);
}
while (!validate_name.is_empty()) {
@ -4712,9 +4956,25 @@ void EditorInspector::_clear_current_favorites() {
update_tree();
}
void EditorInspector::_update_theme() {
updating_theme = true;
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
updating_theme = false;
}
void EditorInspector::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
if (property_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED) {
update_tree_pending = true;
}
} break;
case NOTIFICATION_THEME_CHANGED: {
if (updating_theme) {
break;
}
favorites_category->icon = get_editor_theme_icon(SNAME("Favorites"));
int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"));
@ -4729,14 +4989,15 @@ void EditorInspector::_notification(int p_what) {
ERR_FAIL_NULL(EditorFeatureProfileManager::get_singleton());
EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &EditorInspector::_feature_profile_changed));
set_process(is_visible_in_tree());
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
get_parent()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorInspector::_update_theme));
_update_theme();
if (!is_sub_inspector()) {
get_tree()->connect("node_removed", callable_mp(this, &EditorInspector::_node_removed));
}
} break;
Viewport *viewport = get_viewport();
ERR_FAIL_NULL(viewport);
viewport->connect("gui_focus_changed", callable_mp(this, &EditorInspector::_gui_focus_changed));
case NOTIFICATION_EXIT_TREE: {
get_parent()->disconnect(SceneStringName(theme_changed), callable_mp(this, &EditorInspector::_update_theme));
} break;
case NOTIFICATION_PREDELETE: {
@ -4798,25 +5059,20 @@ void EditorInspector::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
bool needs_update = false;
if (!is_sub_inspector() && EditorThemeManager::is_generated_theme_outdated()) {
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
_update_theme();
}
if (use_settings_name_style && EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/localize_settings")) {
EditorPropertyNameProcessor::Style style = EditorPropertyNameProcessor::get_settings_style();
if (property_name_style != style) {
property_name_style = style;
needs_update = true;
update_tree_pending = true;
}
}
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/inspector")) {
needs_update = true;
}
if (needs_update) {
update_tree();
update_tree_pending = true;
}
} break;
}
@ -4975,6 +5231,7 @@ EditorInspector::EditorInspector() {
base_vbox->add_child(main_vbox);
set_horizontal_scroll_mode(SCROLL_MODE_DISABLED);
set_follow_focus(true);
changing = 0;
search_box = nullptr;
@ -5001,4 +5258,5 @@ EditorInspector::EditorInspector() {
set_property_name_style(EditorPropertyNameProcessor::get_singleton()->get_settings_style());
set_draw_focus_border(true);
set_scroll_on_drag_hover(true);
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_INSPECTOR_H
#define EDITOR_INSPECTOR_H
#pragma once
#include "editor/add_metadata_dialog.h"
#include "editor_property_name_processor.h"
@ -67,6 +66,8 @@ public:
MENU_COPY_PROPERTY_PATH,
MENU_FAVORITE_PROPERTY,
MENU_PIN_VALUE,
MENU_DELETE,
MENU_REVERT_VALUE,
MENU_OPEN_DOCUMENTATION,
};
@ -159,6 +160,9 @@ protected:
void _update_property_bg();
void _accessibility_action_menu(const Variant &p_data);
void _accessibility_action_click(const Variant &p_data);
public:
void emit_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field = StringName(), bool p_changing = false);
@ -184,6 +188,7 @@ public:
ERR_FAIL_NULL_V(object, Variant());
return object->get(property);
}
Variant get_edited_property_display_value() const;
EditorInspector *get_parent_inspector() const;
void set_doc_path(const String &p_doc_path);
@ -314,11 +319,15 @@ protected:
void _notification(int p_what);
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _accessibility_action_menu(const Variant &p_data);
public:
void set_as_favorite(EditorInspector *p_for_inspector);
virtual Size2 get_minimum_size() const override;
virtual Control *make_custom_tooltip(const String &p_text) const override;
EditorInspectorCategory();
};
class EditorInspectorSection : public Container {
@ -349,15 +358,20 @@ protected:
static void _bind_methods();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _accessibility_action_collapse(const Variant &p_data);
void _accessibility_action_expand(const Variant &p_data);
public:
virtual Size2 get_minimum_size() const override;
void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0, int p_level = 1);
String get_section() const;
String get_label() const { return label; }
VBoxContainer *get_vbox();
void unfold();
void fold();
void set_bg_color(const Color &p_bg_color);
void reset_timer();
bool has_revertable_properties() const;
void property_can_revert_changed(const String &p_path, bool p_can_revert);
@ -366,6 +380,18 @@ public:
~EditorInspectorSection();
};
class ArrayPanelContainer : public PanelContainer {
GDCLASS(ArrayPanelContainer, PanelContainer);
protected:
void _notification(int p_what);
void _accessibility_action_menu(const Variant &p_data);
public:
ArrayPanelContainer();
};
class EditorInspectorArray : public EditorInspectorSection {
GDCLASS(EditorInspectorArray, EditorInspectorSection);
@ -379,6 +405,7 @@ class EditorInspectorArray : public EditorInspectorSection {
String swap_method;
int count = 0;
int selected = -1;
VBoxContainer *elements_vbox = nullptr;
@ -442,6 +469,8 @@ class EditorInspectorArray : public EditorInspectorSection {
void _panel_draw(int p_index);
void _panel_gui_input(Ref<InputEvent> p_event, int p_index);
void _panel_gui_focus(int p_index);
void _panel_gui_unfocus(int p_index);
void _move_element(int p_element_index, int p_to_pos);
void _clear_array();
void _resize_array(int p_size);
@ -470,6 +499,8 @@ public:
void setup_with_count_property(Object *p_object, const String &p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_is_const = false, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");
VBoxContainer *get_vbox(int p_index);
void show_menu(int p_index, const Vector2 &p_offset);
EditorInspectorArray(bool p_read_only);
};
@ -568,6 +599,8 @@ class EditorInspector : public ScrollContainer {
int property_focusable;
int update_scroll_request;
bool updating_theme = false;
struct DocCacheInfo {
String doc_path;
String theme_item_name;
@ -605,8 +638,9 @@ class EditorInspector : public ScrollContainer {
void _set_property_favorited(const String &p_path, bool p_favorited);
void _clear_current_favorites();
void _update_theme();
void _node_removed(Node *p_node);
void _gui_focus_changed(Control *p_control);
HashMap<StringName, int> per_array_page;
void _page_change_request(int p_new_page, const StringName &p_array_prefix);
@ -706,5 +740,3 @@ public:
EditorInspector();
};
#endif // EDITOR_INSPECTOR_H

View file

@ -186,8 +186,8 @@ Vector<Ref<Texture2D>> EditorInterface::make_mesh_previews(const Vector<Ref<Mesh
Vector3 ofs = aabb.get_center();
aabb.position -= ofs;
Transform3D xform;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI / 6);
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI / 6) * xform.basis;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math::PI / 6);
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math::PI / 6) * xform.basis;
AABB rot_aabb = xform.xform(aabb);
float m = MAX(rot_aabb.size.x, rot_aabb.size.y) * 0.5;
if (m == 0) {
@ -282,8 +282,8 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
Vector3 center = scene_aabb.get_center();
float camera_size = scene_aabb.get_longest_axis_size();
const float cam_rot_x = -Math_PI / 4;
const float cam_rot_y = -Math_PI / 4;
const float cam_rot_x = -Math::PI / 4;
const float cam_rot_y = -Math::PI / 4;
camera->set_orthogonal(camera_size * 2.0, 0.0001, camera_size * 2.0);
@ -295,8 +295,8 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
camera->set_transform(xf);
Transform3D xform;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI / 6);
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI / 6) * xform.basis;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math::PI / 6);
xform.basis = Basis().rotated(Vector3(1, 0, 0), Math::PI / 6) * xform.basis;
light->set_transform(xform * Transform3D().looking_at(Vector3(-2, -1, -1), Vector3(0, 1, 0)));
light2->set_transform(xform * Transform3D().looking_at(Vector3(+1, -1, -2), Vector3(0, 1, 0)));
@ -647,8 +647,7 @@ void EditorInterface::open_scene_from_path(const String &scene_path, bool p_set_
if (EditorNode::get_singleton()->is_changing_scene()) {
return;
}
EditorNode::get_singleton()->open_request(scene_path, p_set_inherited);
EditorNode::get_singleton()->load_scene(scene_path, false, p_set_inherited);
}
void EditorInterface::reload_scene_from_path(const String &scene_path) {
@ -667,12 +666,24 @@ PackedStringArray EditorInterface::get_open_scenes() const {
PackedStringArray ret;
Vector<EditorData::EditedScene> scenes = EditorNode::get_editor_data().get_edited_scenes();
int scns_amount = scenes.size();
for (int idx_scn = 0; idx_scn < scns_amount; idx_scn++) {
if (scenes[idx_scn].root == nullptr) {
for (EditorData::EditedScene &edited_scene : scenes) {
if (edited_scene.root == nullptr) {
continue;
}
ret.push_back(scenes[idx_scn].root->get_scene_file_path());
ret.push_back(edited_scene.root->get_scene_file_path());
}
return ret;
}
TypedArray<Node> EditorInterface::get_open_scene_roots() const {
TypedArray<Node> ret;
Vector<EditorData::EditedScene> scenes = EditorNode::get_editor_data().get_edited_scenes();
for (EditorData::EditedScene &edited_scene : scenes) {
if (edited_scene.root == nullptr) {
continue;
}
ret.push_back(edited_scene.root);
}
return ret;
}
@ -736,12 +747,11 @@ bool EditorInterface::is_movie_maker_enabled() const {
return EditorRunBar::get_singleton()->is_movie_maker_enabled();
}
#ifdef TOOLS_ENABLED
void EditorInterface::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
const String pf = p_function;
if (p_idx == 0) {
if (pf == "set_main_screen_editor") {
for (String E : { "\"2D\"", "\"3D\"", "\"Script\"", "\"AssetLib\"" }) {
for (String E : { "\"2D\"", "\"3D\"", "\"Script\"", "\"Game\"", "\"AssetLib\"" }) {
r_options->push_back(E);
}
} else if (pf == "get_editor_viewport_3d") {
@ -752,7 +762,6 @@ void EditorInterface::get_argument_options(const StringName &p_function, int p_i
}
Object::get_argument_options(p_function, p_idx, r_options);
}
#endif
// Base.
@ -830,6 +839,7 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("reload_scene_from_path", "scene_filepath"), &EditorInterface::reload_scene_from_path);
ClassDB::bind_method(D_METHOD("get_open_scenes"), &EditorInterface::get_open_scenes);
ClassDB::bind_method(D_METHOD("get_open_scene_roots"), &EditorInterface::get_open_scene_roots);
ClassDB::bind_method(D_METHOD("get_edited_scene_root"), &EditorInterface::get_edited_scene_root);
ClassDB::bind_method(D_METHOD("save_scene"), &EditorInterface::save_scene);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_INTERFACE_H
#define EDITOR_INTERFACE_H
#pragma once
#include "core/io/resource.h"
#include "core/object/class_db.h"
@ -171,6 +170,7 @@ public:
void reload_scene_from_path(const String &scene_path);
PackedStringArray get_open_scenes() const;
TypedArray<Node> get_open_scene_roots() const;
Node *get_edited_scene_root() const;
Error save_scene();
@ -190,9 +190,7 @@ public:
void set_movie_maker_enabled(bool p_enabled);
bool is_movie_maker_enabled() const;
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif
// Base.
static void create();
@ -200,5 +198,3 @@ public:
EditorInterface();
};
#endif // EDITOR_INTERFACE_H

View file

@ -128,6 +128,7 @@ EditorLayoutsDialog::EditorLayoutsDialog() {
name = memnew(LineEdit);
makevb->add_child(name);
name->set_placeholder(TTR("Or enter new layout name"));
name->set_accessibility_name(TTRC("Layout Name"));
name->set_offset(SIDE_TOP, 5);
name->set_anchor_and_offset(SIDE_LEFT, Control::ANCHOR_BEGIN, 5);
name->set_anchor_and_offset(SIDE_RIGHT, Control::ANCHOR_END, -5);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_LAYOUTS_DIALOG_H
#define EDITOR_LAYOUTS_DIALOG_H
#pragma once
#include "scene/gui/dialogs.h"
@ -57,5 +56,3 @@ public:
void set_name_line_enabled(bool p_enabled);
};
#endif // EDITOR_LAYOUTS_DIALOG_H

View file

@ -396,6 +396,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
HBoxContainer *hb_filter = memnew(HBoxContainer);
{
filter_mode = memnew(OptionButton);
filter_mode->set_accessibility_name(TTRC("Locale Filter"));
filter_mode->add_item(TTR("Show All Locales"), SHOW_ALL_LOCALES);
filter_mode->set_h_size_flags(Control::SIZE_EXPAND_FILL);
filter_mode->add_item(TTR("Show Selected Locales Only"), SHOW_ONLY_SELECTED_LOCALES);
@ -434,6 +435,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
}
{
lang_list = memnew(Tree);
lang_list->set_accessibility_name(TTRC("Language"));
lang_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
lang_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
lang_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected));
@ -454,6 +456,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
}
{
script_list = memnew(Tree);
script_list->set_accessibility_name(TTR("Script", "Locale"));
script_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
script_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected));
@ -473,6 +476,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
}
{
cnt_list = memnew(Tree);
cnt_list->set_accessibility_name(TTRC("Country"));
cnt_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
cnt_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
cnt_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected));
@ -499,7 +503,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
{
lang_code = memnew(LineEdit);
lang_code->set_max_length(3);
lang_code->set_tooltip_text("Language");
lang_code->set_accessibility_name("Language");
vb_language->add_child(lang_code);
}
hb_locale->add_child(vb_language);
@ -516,7 +520,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
{
script_code = memnew(LineEdit);
script_code->set_max_length(4);
script_code->set_tooltip_text("Script");
script_code->set_accessibility_name("Script");
vb_script->add_child(script_code);
}
hb_locale->add_child(vb_script);
@ -549,7 +553,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
variant_code = memnew(LineEdit);
variant_code->set_h_size_flags(Control::SIZE_EXPAND_FILL);
variant_code->set_placeholder("Variant");
variant_code->set_tooltip_text("Variant");
variant_code->set_accessibility_name("Variant");
vb_variant->add_child(variant_code);
}
hb_locale->add_child(vb_variant);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_LOCALE_DIALOG_H
#define EDITOR_LOCALE_DIALOG_H
#pragma once
#include "scene/gui/dialogs.h"
@ -85,5 +84,3 @@ public:
void set_locale(const String &p_locale);
void popup_locale_dialog();
};
#endif // EDITOR_LOCALE_DIALOG_H

View file

@ -466,6 +466,7 @@ EditorLog::EditorLog() {
search_box = memnew(LineEdit);
search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
search_box->set_placeholder(TTR("Filter Messages"));
search_box->set_accessibility_name(TTRC("Filter Messages"));
search_box->set_clear_button_enabled(true);
search_box->set_visible(true);
search_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorLog::_search_changed));
@ -481,6 +482,7 @@ EditorLog::EditorLog() {
// Clear.
clear_button = memnew(Button);
clear_button->set_accessibility_name(TTRC("Clear Log"));
clear_button->set_theme_type_variation(SceneStringName(FlatButton));
clear_button->set_focus_mode(FOCUS_NONE);
clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTRC("Clear Output"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::K));
@ -489,6 +491,7 @@ EditorLog::EditorLog() {
// Copy.
copy_button = memnew(Button);
copy_button->set_accessibility_name(TTRC("Copy Selection"));
copy_button->set_theme_type_variation(SceneStringName(FlatButton));
copy_button->set_focus_mode(FOCUS_NONE);
copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTRC("Copy Selection"), KeyModifierMask::CMD_OR_CTRL | Key::C));
@ -509,6 +512,7 @@ EditorLog::EditorLog() {
collapse_button->set_theme_type_variation(SceneStringName(FlatButton));
collapse_button->set_focus_mode(FOCUS_NONE);
collapse_button->set_tooltip_text(TTR("Collapse duplicate messages into one log entry. Shows number of occurrences."));
collapse_button->set_accessibility_name(TTRC("Collapse Duplicate Messages"));
collapse_button->set_toggle_mode(true);
collapse_button->set_pressed(false);
collapse_button->connect(SceneStringName(toggled), callable_mp(this, &EditorLog::_set_collapse));
@ -516,6 +520,7 @@ EditorLog::EditorLog() {
// Show Search.
show_search_button = memnew(Button);
show_search_button->set_accessibility_name(TTRC("Show Search"));
show_search_button->set_theme_type_variation(SceneStringName(FlatButton));
show_search_button->set_focus_mode(FOCUS_NONE);
show_search_button->set_toggle_mode(true);
@ -529,27 +534,27 @@ EditorLog::EditorLog() {
vb_right->add_child(memnew(HSeparator));
LogFilter *std_filter = memnew(LogFilter(MSG_TYPE_STD));
std_filter->initialize_button(TTR("Toggle visibility of standard output messages."), callable_mp(this, &EditorLog::_set_filter_active));
std_filter->initialize_button(TTRC("Standard Messages"), TTRC("Toggle visibility of standard output messages."), callable_mp(this, &EditorLog::_set_filter_active));
vb_right->add_child(std_filter->toggle_button);
type_filter_map.insert(MSG_TYPE_STD, std_filter);
type_filter_map.insert(MSG_TYPE_STD_RICH, std_filter);
LogFilter *error_filter = memnew(LogFilter(MSG_TYPE_ERROR));
error_filter->initialize_button(TTR("Toggle visibility of errors."), callable_mp(this, &EditorLog::_set_filter_active));
error_filter->initialize_button(TTRC("Errors"), TTRC("Toggle visibility of errors."), callable_mp(this, &EditorLog::_set_filter_active));
vb_right->add_child(error_filter->toggle_button);
type_filter_map.insert(MSG_TYPE_ERROR, error_filter);
LogFilter *warning_filter = memnew(LogFilter(MSG_TYPE_WARNING));
warning_filter->initialize_button(TTR("Toggle visibility of warnings."), callable_mp(this, &EditorLog::_set_filter_active));
warning_filter->initialize_button(TTRC("Warnings"), TTRC("Toggle visibility of warnings."), callable_mp(this, &EditorLog::_set_filter_active));
vb_right->add_child(warning_filter->toggle_button);
type_filter_map.insert(MSG_TYPE_WARNING, warning_filter);
LogFilter *editor_filter = memnew(LogFilter(MSG_TYPE_EDITOR));
editor_filter->initialize_button(TTR("Toggle visibility of editor messages."), callable_mp(this, &EditorLog::_set_filter_active));
editor_filter->initialize_button(TTRC("Editor Messages"), TTRC("Toggle visibility of editor messages."), callable_mp(this, &EditorLog::_set_filter_active));
vb_right->add_child(editor_filter->toggle_button);
type_filter_map.insert(MSG_TYPE_EDITOR, editor_filter);
add_message(VERSION_FULL_NAME " (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.");
add_message(GODOT_VERSION_FULL_NAME " (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.");
eh.errfunc = _error_handler;
eh.userdata = this;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_LOG_H
#define EDITOR_LOG_H
#pragma once
#include "core/os/thread.h"
#include "scene/gui/box_container.h"
@ -88,12 +87,13 @@ private:
MessageType type;
Button *toggle_button = nullptr;
void initialize_button(const String &p_tooltip, Callable p_toggled_callback) {
void initialize_button(const String &p_name, const String &p_tooltip, Callable p_toggled_callback) {
toggle_button = memnew(Button);
toggle_button->set_toggle_mode(true);
toggle_button->set_pressed(true);
toggle_button->set_text(itos(message_count));
toggle_button->set_tooltip_text(TTR(p_tooltip));
toggle_button->set_accessibility_name(TTRGET(p_name));
toggle_button->set_tooltip_text(TTRGET(p_tooltip));
toggle_button->set_focus_mode(FOCUS_NONE);
// When toggled call the callback and pass the MessageType this button is for.
toggle_button->connect(SceneStringName(toggled), p_toggled_callback.bind(type));
@ -191,5 +191,3 @@ public:
};
VARIANT_ENUM_CAST(EditorLog::MessageType);
#endif // EDITOR_LOG_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_MAIN_SCREEN_H
#define EDITOR_MAIN_SCREEN_H
#pragma once
#include "scene/gui/panel_container.h"
@ -90,5 +89,3 @@ public:
EditorMainScreen();
};
#endif // EDITOR_MAIN_SCREEN_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_NATIVE_SHADER_SOURCE_VISUALIZER_H
#define EDITOR_NATIVE_SHADER_SOURCE_VISUALIZER_H
#pragma once
#include "scene/gui/dialogs.h"
#include "scene/gui/tab_container.h"
@ -49,5 +48,3 @@ protected:
public:
EditorNativeShaderSourceVisualizer();
};
#endif // EDITOR_NATIVE_SHADER_SOURCE_VISUALIZER_H

File diff suppressed because it is too large Load diff

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_NODE_H
#define EDITOR_NODE_H
#pragma once
#include "core/object/script_language.h"
#include "core/templates/safe_refcount.h"
@ -98,10 +97,7 @@ class ProgressDialog;
class ProjectExportDialog;
class ProjectSettingsEditor;
class SceneImportSettingsDialog;
class SurfaceUpgradeTool;
class SurfaceUpgradeDialog;
class UIDUpgradeTool;
class UIDUpgradeDialog;
class ProjectUpgradeTool;
struct EditorProgress {
String task;
@ -121,6 +117,7 @@ public:
SCENE_NAME_CASING_PASCAL_CASE,
SCENE_NAME_CASING_SNAKE_CASE,
SCENE_NAME_CASING_KEBAB_CASE,
SCENE_NAME_CASING_CAMEL_CASE,
};
enum ActionOnPlay {
@ -144,6 +141,7 @@ public:
FILE_SAVE_SCENE,
FILE_SAVE_AS_SCENE,
FILE_SAVE_ALL_SCENES,
FILE_MULTI_SAVE_AS_SCENE,
FILE_QUICK_OPEN,
FILE_QUICK_OPEN_SCENE,
FILE_QUICK_OPEN_SCRIPT,
@ -167,8 +165,7 @@ public:
TOOLS_ORPHAN_RESOURCES,
TOOLS_BUILD_PROFILE_MANAGER,
TOOLS_SURFACE_UPGRADE,
TOOLS_UID_UPGRADE,
TOOLS_PROJECT_UPGRADE,
TOOLS_CUSTOM,
VCS_METADATA,
@ -227,7 +224,6 @@ public:
private:
friend class EditorSceneTabs;
friend class SurfaceUpgradeTool;
enum {
MAX_INIT_CALLBACKS = 128,
@ -301,6 +297,7 @@ private:
int tab_closing_idx = 0;
List<String> tabs_to_close;
List<int> scenes_to_save_as;
int tab_closing_menu_option = -1;
bool exiting = false;
@ -351,14 +348,16 @@ private:
PopupMenu *recent_scenes = nullptr;
String _recent_scene;
List<String> previous_scenes;
List<String> prev_closed_scenes;
String defer_load_scene;
Node *_last_instantiated_scene = nullptr;
ConfirmationDialog *confirmation = nullptr;
Button *confirmation_button = nullptr;
ConfirmationDialog *save_confirmation = nullptr;
ConfirmationDialog *import_confirmation = nullptr;
ConfirmationDialog *pick_main_scene = nullptr;
ConfirmationDialog *open_project_settings = nullptr;
Button *select_current_scene_button = nullptr;
AcceptDialog *accept = nullptr;
AcceptDialog *save_accept = nullptr;
@ -387,6 +386,7 @@ private:
EditorFileDialog *file_export_lib = nullptr;
EditorFileDialog *file_script = nullptr;
EditorFileDialog *file_android_build_source = nullptr;
EditorFileDialog *file_pack_zip = nullptr;
String current_path;
MenuButton *update_spinner = nullptr;
@ -414,15 +414,15 @@ private:
Tree *disk_changed_list = nullptr;
ConfirmationDialog *disk_changed = nullptr;
ConfirmationDialog *project_data_missing = nullptr;
bool scene_distraction_free = false;
bool script_distraction_free = false;
bool changing_scene = false;
bool cmdline_export_mode = false;
bool cmdline_mode = false;
bool convert_old = false;
bool immediate_dialog_confirmed = false;
bool opening_prev = false;
bool restoring_scenes = false;
bool unsaved_cache = true;
@ -461,16 +461,8 @@ private:
HashMap<String, Ref<Texture2D>> icon_type_cache;
SurfaceUpgradeTool *surface_upgrade_tool = nullptr;
SurfaceUpgradeDialog *surface_upgrade_dialog = nullptr;
bool run_surface_upgrade_tool = false;
UIDUpgradeTool *uid_upgrade_tool = nullptr;
UIDUpgradeDialog *uid_upgrade_dialog = nullptr;
bool run_uid_upgrade_tool = false;
bool should_prompt_uid_upgrade_tool = false;
ProjectUpgradeTool *project_upgrade_tool = nullptr;
bool run_project_upgrade_tool = false;
bool was_window_windowed_last = false;
@ -537,7 +529,6 @@ private:
void _tool_menu_option(int p_idx);
void _export_as_menu_option(int p_idx);
void _update_file_menu_opened();
void _update_file_menu_closed();
void _palette_quick_open_dialog();
void _remove_plugin_from_enabled(const String &p_name);
@ -587,9 +578,12 @@ private:
void _project_run_started();
void _project_run_stopped();
void _update_prev_closed_scenes(const String &p_scene_path, bool p_add_scene);
void _add_to_recent_scenes(const String &p_scene);
void _update_recent_scenes();
void _open_recent_scene(int p_idx);
void _dropped_files(const Vector<String> &p_files);
void _add_dropped_files_recursive(const Vector<String> &p_files, String to_path);
@ -615,6 +609,7 @@ private:
bool _find_and_save_edited_subresources(Object *obj, HashMap<Ref<Resource>, bool> &processed, int32_t flags);
void _save_edited_subresources(Node *scene, HashMap<Ref<Resource>, bool> &processed, int32_t flags);
void _mark_unsaved_scenes();
bool _is_scene_unsaved(int p_idx);
void _find_node_types(Node *p_node, int &count_2d, int &count_3d);
void _save_scene_with_preview(String p_file, int p_idx = -1);
@ -623,6 +618,7 @@ private:
bool _find_scene_in_use(Node *p_node, const String &p_path) const;
void _proceed_closing_scene_tabs();
void _proceed_save_asing_scene_tabs();
bool _is_closing_editor() const;
void _restart_editor(bool p_goto_project_manager = false);
@ -662,6 +658,7 @@ private:
bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class);
Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const String &p_script_path, const String &p_fallback = "Object", bool p_fallback_script_to_theme = false);
Ref<Texture2D> _get_editor_theme_native_menu_icon(const StringName &p_name, bool p_global_menu, bool p_dark_mode) const;
void _pick_main_scene_custom_action(const String &p_custom_action_name);
@ -678,6 +675,8 @@ private:
void _execute_upgrades();
bool _is_project_data_missing();
protected:
friend class FileSystemDock;
@ -744,7 +743,7 @@ public:
ProjectSettingsEditor *get_project_settings() { return project_settings_editor; }
void trigger_menu_option(int p_option, bool p_confirmed);
bool has_previous_scenes() const;
bool has_previous_closed_scenes() const;
void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); }
@ -778,7 +777,6 @@ public:
void replace_resources_in_scenes(
const Vector<Ref<Resource>> &p_source_resources,
const Vector<Ref<Resource>> &p_target_resource);
void open_request(const String &p_path, bool p_set_inherited = false);
void edit_foreign_resource(Ref<Resource> p_resource);
bool is_resource_read_only(Ref<Resource> p_resource, bool p_foreign_resources_are_writable = false);
@ -797,6 +795,7 @@ public:
int new_scene();
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
Error load_scene_or_resource(const String &p_file, bool p_ignore_broken_deps = false, bool p_change_scene_tab_if_already_open = true);
HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node, bool p_node_references_only);
HashMap<StringName, Variant> get_modified_properties_reference_to_nodes(Node *p_node, List<Node *> &p_nodes_referenced_by);
@ -966,6 +965,7 @@ public:
Vector<Ref<EditorResourceConversionPlugin>> find_resource_conversion_plugin_for_type_name(const String &p_type);
bool ensure_main_scene(bool p_from_native);
bool validate_custom_directory();
};
class EditorPluginList : public Object {
@ -993,9 +993,6 @@ public:
void remove_plugin(EditorPlugin *p_plugin);
void clear();
bool is_empty();
EditorPluginList();
~EditorPluginList();
};
struct EditorProgressBG {
@ -1007,5 +1004,3 @@ struct EditorProgressBG {
}
~EditorProgressBG() { EditorNode::progress_end_task_bg(task); }
};
#endif // EDITOR_NODE_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PATHS_H
#define EDITOR_PATHS_H
#pragma once
#include "core/object/class_db.h"
#include "core/object/object.h"
@ -84,5 +83,3 @@ public:
EditorPaths();
};
#endif // EDITOR_PATHS_H

View file

@ -236,6 +236,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
hb->add_child(text);
text->set_h_size_flags(SIZE_EXPAND_FILL);
open_big_text = memnew(Button);
open_big_text->set_accessibility_name(TTRC("Open Text Edit Dialog"));
open_big_text->set_flat(true);
open_big_text->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyMultilineText::_open_big_text));
hb->add_child(open_big_text);
@ -373,6 +374,7 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() {
hb->add_child(edit_custom_layout);
option_button = memnew(OptionButton);
option_button->set_accessibility_name(TTRC("Enum Options"));
option_button->set_h_size_flags(SIZE_EXPAND_FILL);
option_button->set_clip_text(true);
option_button->set_flat(true);
@ -381,22 +383,26 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() {
option_button->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyTextEnum::_option_selected));
edit_button = memnew(Button);
edit_button->set_accessibility_name(TTRC("Edit"));
edit_button->set_flat(true);
edit_button->hide();
default_layout->add_child(edit_button);
edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_edit_custom_value));
custom_value_edit = memnew(LineEdit);
custom_value_edit->set_accessibility_name(TTRC("Custom Value"));
custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit_custom_layout->add_child(custom_value_edit);
custom_value_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted));
accept_button = memnew(Button);
accept_button->set_accessibility_name(TTRC("Accept Custom Value Edit"));
accept_button->set_flat(true);
edit_custom_layout->add_child(accept_button);
accept_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_accepted));
cancel_button = memnew(Button);
cancel_button->set_accessibility_name(TTRC("Cancel Custom Value Edit"));
cancel_button->set_flat(true);
edit_custom_layout->add_child(cancel_button);
cancel_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_canceled));
@ -453,12 +459,14 @@ EditorPropertyLocale::EditorPropertyLocale() {
HBoxContainer *locale_hb = memnew(HBoxContainer);
add_child(locale_hb);
locale = memnew(LineEdit);
locale->set_accessibility_name(TTRC("Locale"));
locale_hb->add_child(locale);
locale->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyLocale::_locale_selected));
locale->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyLocale::_locale_focus_exited));
locale->set_h_size_flags(SIZE_EXPAND_FILL);
locale_edit = memnew(Button);
locale_edit->set_accessibility_name(TTRC("Edit"));
locale_edit->set_clip_text(true);
locale_hb->add_child(locale_edit);
add_focusable(locale);
@ -573,7 +581,7 @@ void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_d
return;
}
const Vector<String> filesPaths = drag_data["files"];
if (filesPaths.size() == 0) {
if (filesPaths.is_empty()) {
return;
}
@ -589,12 +597,12 @@ bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant
return false;
}
const Vector<String> filesPaths = drag_data["files"];
if (filesPaths.size() == 0) {
if (filesPaths.is_empty()) {
return false;
}
for (const String &extension : extensions) {
if (filesPaths[0].ends_with(extension.substr(1, extension.size() - 1))) {
if (filesPaths[0].ends_with(extension.substr(1))) {
return true;
}
}
@ -606,6 +614,7 @@ EditorPropertyPath::EditorPropertyPath() {
HBoxContainer *path_hb = memnew(HBoxContainer);
add_child(path_hb);
path = memnew(LineEdit);
path->set_accessibility_name(TTRC("Path"));
SET_DRAG_FORWARDING_CDU(path, EditorPropertyPath);
path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
path_hb->add_child(path);
@ -614,6 +623,7 @@ EditorPropertyPath::EditorPropertyPath() {
path->set_h_size_flags(SIZE_EXPAND_FILL);
path_edit = memnew(Button);
path_edit->set_accessibility_name(TTRC("Edit"));
path_edit->set_clip_text(true);
path_hb->add_child(path_edit);
add_focusable(path);
@ -993,6 +1003,15 @@ void EditorPropertyLayersGrid::gui_input(const Ref<InputEvent> &p_ev) {
void EditorPropertyLayersGrid::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
RID ae = get_accessibility_element();
ERR_FAIL_COND(ae.is_null());
//TODO
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Layers grid property editor")));
} break;
case NOTIFICATION_DRAW: {
Size2 grid_size = get_grid_size();
grid_size.x = get_size().x;
@ -1300,6 +1319,7 @@ EditorPropertyLayers::EditorPropertyLayers() {
hb->add_child(grid);
button = memnew(TextureButton);
button->set_accessibility_name(TTRC("Layers"));
button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
button->set_toggle_mode(true);
button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLayers::_button_pressed));
@ -1326,7 +1346,7 @@ void EditorPropertyInteger::_value_changed(int64_t val) {
}
void EditorPropertyInteger::update_property() {
int64_t val = get_edited_property_value();
int64_t val = get_edited_property_display_value();
spin->set_value_no_signal(val);
#ifdef DEBUG_ENABLED
// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.
@ -1391,6 +1411,7 @@ void EditorPropertyObjectID::setup(const String &p_base_type) {
EditorPropertyObjectID::EditorPropertyObjectID() {
edit = memnew(Button);
edit->set_accessibility_name(TTRC("Edit"));
add_child(edit);
add_focusable(edit);
edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
@ -1416,6 +1437,7 @@ void EditorPropertySignal::update_property() {
EditorPropertySignal::EditorPropertySignal() {
edit = memnew(Button);
edit->set_accessibility_name(TTRC("Edit"));
add_child(edit);
add_focusable(edit);
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySignal::_edit_pressed));
@ -1435,6 +1457,7 @@ void EditorPropertyCallable::update_property() {
EditorPropertyCallable::EditorPropertyCallable() {
edit = memnew(Button);
edit->set_accessibility_name(TTRC("Edit"));
add_child(edit);
add_focusable(edit);
}
@ -1766,6 +1789,7 @@ EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
if (grid) {
@ -1860,6 +1884,7 @@ EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {
for (int i = 0; i < 4; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
if (grid) {
@ -1951,6 +1976,7 @@ EditorPropertyPlane::EditorPropertyPlane(bool p_force_wide) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_flat(true);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
bc->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyPlane::_value_changed).bind(desc[i]));
@ -2121,6 +2147,7 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_flat(true);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
default_layout->add_child(spin[i]);
add_focusable(spin[i]);
spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_value_changed).bind(desc[i]));
@ -2148,6 +2175,7 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
euler[i] = memnew(EditorSpinSlider);
euler[i]->set_flat(true);
euler[i]->set_label(desc[i]);
euler[i]->set_accessibility_name(vformat(TTR("Temporary Euler %s"), desc[i]));
edit_custom_layout->add_child(euler[i]);
add_focusable(euler[i]);
euler[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_custom_value_changed));
@ -2157,6 +2185,7 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
}
edit_button = memnew(Button);
edit_button->set_accessibility_name(TTRC("Edit"));
edit_button->set_flat(true);
edit_button->set_toggle_mode(true);
default_layout->add_child(edit_button);
@ -2231,6 +2260,7 @@ EditorPropertyAABB::EditorPropertyAABB() {
for (int i = 0; i < 6; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
g->add_child(spin[i]);
@ -2311,6 +2341,7 @@ EditorPropertyTransform2D::EditorPropertyTransform2D(bool p_include_origin) {
for (int i = 0; i < 6; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
if (p_include_origin || i % 3 != 2) {
g->add_child(spin[i]);
@ -2393,6 +2424,7 @@ EditorPropertyBasis::EditorPropertyBasis() {
for (int i = 0; i < 9; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
@ -2482,6 +2514,7 @@ EditorPropertyTransform3D::EditorPropertyTransform3D() {
for (int i = 0; i < 12; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
@ -2579,6 +2612,7 @@ EditorPropertyProjection::EditorPropertyProjection() {
for (int i = 0; i < 16; i++) {
spin[i] = memnew(EditorSpinSlider);
spin[i]->set_label(desc[i]);
spin[i]->set_accessibility_name(desc[i]);
spin[i]->set_flat(true);
g->add_child(spin[i]);
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
@ -2635,7 +2669,7 @@ void EditorPropertyColor::_notification(int p_what) {
}
void EditorPropertyColor::update_property() {
picker->set_pick_color(get_edited_property_value());
picker->set_pick_color(get_edited_property_display_value());
const Color color = picker->get_pick_color();
// Add a tooltip to display each channel's values without having to click the ColorPickerButton
@ -2732,6 +2766,13 @@ void EditorPropertyNodePath::_node_assign() {
scene_tree->popup_scenetree_dialog(n, get_base_node());
}
void EditorPropertyNodePath::_assign_draw() {
if (dropping) {
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
assign->draw_rect(Rect2(Point2(), assign->get_size()), color, false);
}
}
void EditorPropertyNodePath::_update_menu() {
const NodePath &np = _get_node_path();
@ -2909,6 +2950,20 @@ void EditorPropertyNodePath::_notification(int p_what) {
menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit")));
menu->get_popup()->set_item_icon(ACTION_SELECT, get_editor_theme_icon(SNAME("ExternalLink")));
} break;
case NOTIFICATION_DRAG_BEGIN: {
if (!is_read_only() && is_drop_valid(get_viewport()->gui_get_drag_data())) {
dropping = true;
assign->queue_redraw();
}
} break;
case NOTIFICATION_DRAG_END: {
if (dropping) {
dropping = false;
assign->queue_redraw();
}
} break;
}
}
@ -2943,12 +2998,14 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
hbc->add_theme_constant_override("separation", 0);
add_child(hbc);
assign = memnew(Button);
assign->set_accessibility_name(TTRC("Assign Node"));
assign->set_flat(true);
assign->set_h_size_flags(SIZE_EXPAND_FILL);
assign->set_clip_text(true);
assign->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
assign->set_expand_icon(true);
assign->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyNodePath::_node_assign));
assign->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyNodePath::_assign_draw));
SET_DRAG_FORWARDING_CD(assign, EditorPropertyNodePath);
hbc->add_child(assign);
@ -2964,6 +3021,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyNodePath::_menu_option));
edit = memnew(LineEdit);
edit->set_accessibility_name(TTRC("Node Path"));
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->hide();
edit->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyNodePath::_accept_text));
@ -3015,7 +3073,7 @@ void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource,
bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property());
get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold);
update_property();
} else {
} else if (!is_checkable() || is_checked()) {
emit_signal(SNAME("resource_selected"), get_edited_property(), p_resource);
}
}
@ -3036,15 +3094,14 @@ static bool _find_recursive_resources(const Variant &v, HashSet<Resource *> &res
} break;
case Variant::DICTIONARY: {
Dictionary d = v;
List<Variant> keys;
d.get_key_list(&keys);
for (const Variant &k : keys) {
for (const KeyValue<Variant, Variant> &kv : d) {
const Variant &k = kv.key;
const Variant &v2 = kv.value;
if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {
if (_find_recursive_resources(k, resources_found)) {
return true;
}
}
Variant v2 = d[k];
if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {
if (_find_recursive_resources(v2, resources_found)) {
return true;
@ -3228,8 +3285,9 @@ void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
return;
}
Ref<ViewportTexture> vt;
vt.instantiate();
Ref<ViewportTexture> vt = get_edited_property_value();
ERR_FAIL_COND(vt.is_null());
vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));
emit_changed(get_edited_property(), vt);
@ -3276,7 +3334,7 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const
}
void EditorPropertyResource::update_property() {
Ref<Resource> res = get_edited_property_value();
Ref<Resource> res = get_edited_property_display_value();
if (use_sub_inspector) {
if (res.is_valid() != resource_picker->is_toggle_mode()) {
@ -3303,11 +3361,11 @@ void EditorPropertyResource::update_property() {
sub_inspector->set_use_folding(is_using_folding());
sub_inspector->set_draw_focus_border(false);
sub_inspector->set_focus_mode(FocusMode::FOCUS_NONE);
sub_inspector->set_use_filter(use_filter);
sub_inspector->register_text_enter(parent_inspector->search_box);
sub_inspector->set_mouse_filter(MOUSE_FILTER_STOP);
add_child(sub_inspector);
set_bottom_editor(sub_inspector);
@ -3328,6 +3386,8 @@ void EditorPropertyResource::update_property() {
}
}
sub_inspector->set_read_only(is_checkable() && !is_checked());
if (res.ptr() != sub_inspector->get_edited_object()) {
sub_inspector->edit(res.ptr());
_update_property_bg();

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PROPERTIES_H
#define EDITOR_PROPERTIES_H
#pragma once
#include "editor/editor_inspector.h"
@ -629,10 +628,12 @@ class EditorPropertyNodePath : public EditorProperty {
SceneTreeDialog *scene_tree = nullptr;
bool use_path_from_scene_root = false;
bool editing_node = false;
bool dropping = false;
Vector<StringName> valid_types;
void _node_selected(const NodePath &p_path, bool p_absolute = true);
void _node_assign();
void _assign_draw();
Node *get_base_node();
void _update_menu();
void _menu_option(int p_idx);
@ -723,5 +724,3 @@ public:
static EditorProperty *get_editor_for_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false);
};
#endif // EDITOR_PROPERTIES_H

View file

@ -33,6 +33,7 @@
#include "core/input/input.h"
#include "core/io/marshalls.h"
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "editor/editor_properties.h"
#include "editor/editor_properties_vector.h"
#include "editor/editor_settings.h"
@ -52,9 +53,9 @@ bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_
int index;
if (name.begins_with("metadata/")) {
index = name.get_slice("/", 2).to_int();
index = name.get_slicec('/', 2).to_int();
} else {
index = name.get_slice("/", 1).to_int();
index = name.get_slicec('/', 1).to_int();
}
array.set(index, p_value);
@ -70,9 +71,9 @@ bool EditorPropertyArrayObject::_get(const StringName &p_name, Variant &r_ret) c
int index;
if (name.begins_with("metadata/")) {
index = name.get_slice("/", 2).to_int();
index = name.get_slicec('/', 2).to_int();
} else {
index = name.get_slice("/", 1).to_int();
index = name.get_slicec('/', 1).to_int();
}
bool valid;
@ -93,9 +94,6 @@ Variant EditorPropertyArrayObject::get_array() {
return array;
}
EditorPropertyArrayObject::EditorPropertyArrayObject() {
}
///////////////////
bool EditorPropertyDictionaryObject::_set(const StringName &p_name, const Variant &p_value) {
@ -232,9 +230,6 @@ String EditorPropertyDictionaryObject::get_label_for_index(int p_index) {
}
}
EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() {
}
///////////////////// ARRAY ///////////////////////////
void EditorPropertyArray::initialize_array(Variant &p_array) {
@ -263,7 +258,7 @@ void EditorPropertyArray::_property_changed(const String &p_property, Variant p_
p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716.
}
int index = p_property.get_slice("/", 1).to_int();
int index = p_property.get_slicec('/', 1).to_int();
Variant array = object->get_array().duplicate();
array.set(index, p_value);
@ -310,6 +305,7 @@ void EditorPropertyArray::_create_new_property_slot() {
HBoxContainer *hbox = memnew(HBoxContainer);
Button *reorder_button = memnew(Button);
reorder_button->set_accessibility_name(TTRC("Reorder"));
reorder_button->set_button_icon(get_editor_theme_icon(SNAME("TripleBar")));
reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE);
reorder_button->set_disabled(is_read_only());
@ -325,12 +321,14 @@ void EditorPropertyArray::_create_new_property_slot() {
if (is_untyped_array) {
Button *edit_btn = memnew(Button);
edit_btn->set_accessibility_name(TTRC("Edit"));
edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
edit_btn->set_disabled(is_read_only());
edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_change_type).bind(edit_btn, idx));
hbox->add_child(edit_btn);
} else {
Button *remove_btn = memnew(Button);
remove_btn->set_accessibility_name(TTRC("Remove"));
remove_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
remove_btn->set_disabled(is_read_only());
remove_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_remove_pressed).bind(idx));
@ -401,7 +399,7 @@ void EditorPropertyArray::update_property() {
}
if (preview_value) {
String ctr_str = array.get_construct_string().trim_prefix(array_type_name + "(").trim_suffix(")").replace("\n", "");
String ctr_str = array.get_construct_string().trim_prefix(array_type_name + "(").trim_suffix(")").remove_char('\n');
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
int type_end = ctr_str.find("](");
if (type_end > 0) {
@ -430,7 +428,6 @@ void EditorPropertyArray::update_property() {
if (!container) {
container = memnew(PanelContainer);
container->set_mouse_filter(MOUSE_FILTER_STOP);
add_child(container);
set_bottom_editor(container);
@ -450,6 +447,7 @@ void EditorPropertyArray::update_property() {
size_slider->set_editing_integer(true);
size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
size_slider->set_read_only(is_read_only());
size_slider->set_accessibility_name(TTRC("Size"));
size_slider->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyArray::_length_changed));
hbox->add_child(size_slider);
@ -460,7 +458,10 @@ void EditorPropertyArray::update_property() {
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element"));
button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_add_element));
button_add_item->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyArray::_button_add_item_draw));
SET_DRAG_FORWARDING_CD(button_add_item, EditorPropertyArray);
button_add_item->set_disabled(is_read_only());
button_add_item->set_accessibility_name(TTRC("Add"));
vbox->add_child(button_add_item);
paginator = memnew(EditorPaginator);
@ -551,6 +552,13 @@ void EditorPropertyArray::_button_draw() {
}
}
void EditorPropertyArray::_button_add_item_draw() {
if (dropping) {
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
button_add_item->draw_rect(Rect2(Point2(), button_add_item->get_size()), color, false);
}
}
bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
if (is_read_only()) {
return false;
@ -570,14 +578,19 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
if (drop_type == "files") {
PackedStringArray files = drag_data["files"];
for (int i = 0; i < files.size(); i++) {
const String &file = files[i];
String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
for (const String &file : files) {
int idx_in_dir;
EditorFileSystemDirectory const *dir = EditorFileSystem::get_singleton()->find_file(file, &idx_in_dir);
if (!dir) {
return false;
}
StringName ftype = dir->get_file_type(idx_in_dir);
String script_class = dir->get_file_resource_script_class(idx_in_dir);
for (int j = 0; j < allowed_type.get_slice_count(","); j++) {
String at = allowed_type.get_slice(",", j).strip_edges();
for (String at : allowed_type.split(",", false)) {
at = at.strip_edges();
// Fail if one of the files is not of allowed type.
if (!ClassDB::is_parent_class(ftype, at)) {
if (!ClassDB::is_parent_class(ftype, at) && !EditorNode::get_editor_data().script_class_is_parent(script_class, at)) {
return false;
}
}
@ -587,6 +600,28 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
return true;
}
if (drop_type == "resource") {
Ref<Resource> res = drag_data["resource"];
if (res.is_null()) {
return false;
}
String res_type = res->get_class();
StringName script_class;
if (res->get_script()) {
script_class = EditorNode::get_singleton()->get_object_custom_type_name(res->get_script());
}
for (String at : allowed_type.split(",", false)) {
at = at.strip_edges();
if (ClassDB::is_parent_class(res_type, at) || EditorNode::get_editor_data().script_class_is_parent(script_class, at)) {
return true;
}
}
return false;
}
if (drop_type == "nodes") {
Array node_paths = drag_data["nodes"];
@ -595,8 +630,8 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
if (subtype_hint_string == "NodePath") {
return true;
} else {
for (int j = 0; j < subtype_hint_string.get_slice_count(","); j++) {
String ast = subtype_hint_string.get_slice(",", j).strip_edges();
for (String ast : subtype_hint_string.split(",", false)) {
ast = ast.strip_edges();
allowed_subtype_array.append(ast);
}
}
@ -609,7 +644,8 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
ERR_FAIL_NULL_V_MSG(dropped_node, false, "Could not get the dropped node by its path.");
if (allowed_type != "NodePath") {
if (!ClassDB::is_parent_class(dropped_node->get_class_name(), allowed_type)) {
if (!ClassDB::is_parent_class(dropped_node->get_class_name(), allowed_type) &&
!EditorNode::get_singleton()->is_object_of_custom_type(dropped_node, allowed_type)) {
// Fail if one of the nodes is not of allowed type.
return false;
}
@ -620,7 +656,8 @@ bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
if (!allowed_subtype_array.has(dropped_node->get_class_name())) {
// The dropped node type was not found in the allowed subtype array, we must check if it inherits one of them.
for (const String &ast : allowed_subtype_array) {
if (ClassDB::is_parent_class(dropped_node->get_class_name(), ast)) {
if (ClassDB::is_parent_class(dropped_node->get_class_name(), ast) ||
EditorNode::get_singleton()->is_object_of_custom_type(dropped_node, ast)) {
is_drop_allowed = true;
break;
} else {
@ -645,8 +682,6 @@ bool EditorPropertyArray::can_drop_data_fw(const Point2 &p_point, const Variant
}
void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
ERR_FAIL_COND(!_is_drop_valid(p_data));
Dictionary drag_data = p_data;
const String drop_type = drag_data.get("type", "");
Variant array = object->get_array();
@ -674,6 +709,16 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d
emit_changed(get_edited_property(), array);
}
if (drop_type == "resource") {
Ref<Resource> res = drag_data["resource"];
if (res.is_valid()) {
array.call("push_back", res);
emit_changed(get_edited_property(), array);
}
}
if (drop_type == "nodes") {
Array node_paths = drag_data["nodes"];
Node *base_node = get_base_node();
@ -729,6 +774,9 @@ void EditorPropertyArray::_notification(int p_what) {
if (_is_drop_valid(get_viewport()->gui_get_drag_data())) {
dropping = true;
edit->queue_redraw();
if (button_add_item) {
button_add_item->queue_redraw();
}
}
}
} break;
@ -737,6 +785,9 @@ void EditorPropertyArray::_notification(int p_what) {
if (dropping) {
dropping = false;
edit->queue_redraw();
if (button_add_item) {
button_add_item->queue_redraw();
}
}
} break;
}
@ -808,11 +859,11 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint
String subtype_string = p_hint_string.substr(0, hint_subtype_separator);
int slash_pos = subtype_string.find_char('/');
if (slash_pos >= 0) {
subtype_hint = PropertyHint(subtype_string.substr(slash_pos + 1, subtype_string.size() - slash_pos - 1).to_int());
subtype_hint = PropertyHint(subtype_string.substr(slash_pos + 1).to_int());
subtype_string = subtype_string.substr(0, slash_pos);
}
subtype_hint_string = p_hint_string.substr(hint_subtype_separator + 1, p_hint_string.size() - hint_subtype_separator - 1);
subtype_hint_string = p_hint_string.substr(hint_subtype_separator + 1);
subtype = Variant::Type(subtype_string.to_int());
}
}
@ -839,7 +890,7 @@ void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_eve
}
float required_y_distance = 20.0f * EDSCALE;
if (ABS(reorder_mouse_y_delta) > required_y_distance) {
if (Math::abs(reorder_mouse_y_delta) > required_y_distance) {
int direction = reorder_mouse_y_delta > 0.0f ? 1 : -1;
reorder_mouse_y_delta -= required_y_distance * direction;
@ -851,8 +902,12 @@ void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_eve
// Automatically move to the next/previous page.
_page_changed(page_index + direction);
}
// Ensure the moving element is visible.
InspectorDock::get_inspector_singleton()->ensure_control_visible(reorder_slot.container);
// Ensure the moving element is visible in the root inspector.
EditorInspector *parent_inspector = get_parent_inspector();
if (parent_inspector) {
// Defer to prevent moving elements from not displaying properly, especially near borders.
callable_mp((ScrollContainer *)parent_inspector->get_root_inspector(), &ScrollContainer::ensure_control_visible).call_deferred(reorder_slot.container);
}
}
}
}
@ -905,6 +960,7 @@ EditorPropertyArray::EditorPropertyArray() {
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
edit = memnew(Button);
edit->set_accessibility_name(TTRC("Edit"));
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->set_clip_text(true);
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_edit_pressed));
@ -1018,12 +1074,14 @@ void EditorPropertyDictionary::_create_new_property_slot(int p_idx) {
if (is_untyped_dict) {
Button *edit_btn = memnew(Button);
edit_btn->set_accessibility_name(TTRC("Edit"));
edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
edit_btn->set_disabled(is_read_only());
edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_change_type).bind(edit_btn, slots.size()));
hbox->add_child(edit_btn);
} else if (p_idx >= 0) {
Button *remove_btn = memnew(Button);
remove_btn->set_accessibility_name(TTRC("Remove"));
remove_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
remove_btn->set_disabled(is_read_only());
remove_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_remove_pressed).bind(slots.size()));
@ -1091,11 +1149,11 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s
String key_subtype_string = key.substr(0, hint_key_subtype_separator);
int slash_pos = key_subtype_string.find_char('/');
if (slash_pos >= 0) {
key_subtype_hint = PropertyHint(key_subtype_string.substr(slash_pos + 1, key_subtype_string.size() - slash_pos - 1).to_int());
key_subtype_hint = PropertyHint(key_subtype_string.substr(slash_pos + 1).to_int());
key_subtype_string = key_subtype_string.substr(0, slash_pos);
}
key_subtype_hint_string = key.substr(hint_key_subtype_separator + 1, key.size() - hint_key_subtype_separator - 1);
key_subtype_hint_string = key.substr(hint_key_subtype_separator + 1);
key_subtype = Variant::Type(key_subtype_string.to_int());
Variant new_key = object->get_new_item_key();
@ -1110,11 +1168,11 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s
String value_subtype_string = value.substr(0, hint_value_subtype_separator);
int slash_pos = value_subtype_string.find_char('/');
if (slash_pos >= 0) {
value_subtype_hint = PropertyHint(value_subtype_string.substr(slash_pos + 1, value_subtype_string.size() - slash_pos - 1).to_int());
value_subtype_hint = PropertyHint(value_subtype_string.substr(slash_pos + 1).to_int());
value_subtype_string = value_subtype_string.substr(0, slash_pos);
}
value_subtype_hint_string = value.substr(hint_value_subtype_separator + 1, value.size() - hint_value_subtype_separator - 1);
value_subtype_hint_string = value.substr(hint_value_subtype_separator + 1);
value_subtype = Variant::Type(value_subtype_string.to_int());
Variant new_value = object->get_new_item_value();
@ -1179,7 +1237,7 @@ void EditorPropertyDictionary::update_property() {
object->set_dict(updated_val);
if (preview_value) {
String ctr_str = updated_val.get_construct_string().replace("\n", "");
String ctr_str = updated_val.get_construct_string().remove_char('\n');
if (key_subtype != Variant::NIL || value_subtype != Variant::NIL) {
int type_end = ctr_str.find("](");
if (type_end > 0) {
@ -1208,7 +1266,6 @@ void EditorPropertyDictionary::update_property() {
if (!container) {
container = memnew(PanelContainer);
container->set_mouse_filter(MOUSE_FILTER_STOP);
add_child(container);
set_bottom_editor(container);
@ -1239,6 +1296,7 @@ void EditorPropertyDictionary::update_property() {
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Key/Value Pair"));
button_add_item->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
button_add_item->set_disabled(is_read_only());
button_add_item->set_accessibility_name(TTRC("Add"));
button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_add_key_value));
add_vbox->add_child(button_add_item);
}
@ -1302,7 +1360,14 @@ void EditorPropertyDictionary::update_property() {
Variant value;
object->get_by_property_name(slot.prop_name, value);
Variant::Type value_type = value.get_type();
Variant::Type value_type;
if (dict.is_typed_value() && slot.prop_key) {
value_type = value_subtype;
} else {
value_type = value.get_type();
}
// Check if the editor property needs to be updated.
bool value_as_id = Object::cast_to<EncodedObjectAsID>(value);
@ -1433,6 +1498,7 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
edit = memnew(Button);
edit->set_accessibility_name(TTRC("Edit"));
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->set_clip_text(true);
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_edit_pressed));
@ -1462,7 +1528,7 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
void EditorPropertyLocalizableString::_property_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {
if (p_property.begins_with("indices")) {
int index = p_property.get_slice("/", 1).to_int();
int index = p_property.get_slicec('/', 1).to_int();
Dictionary dict = object->get_dict().duplicate();
Variant key = dict.get_key_at_index(index);
@ -1588,6 +1654,7 @@ void EditorPropertyLocalizableString::update_property() {
hbox->add_child(prop);
prop->set_h_size_flags(SIZE_EXPAND_FILL);
Button *edit_btn = memnew(Button);
edit_btn->set_accessibility_name(TTRC("Remove Translation"));
edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
hbox->add_child(edit_btn);
edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocalizableString::_remove_item).bind(edit_btn, remove_index));
@ -1597,6 +1664,7 @@ void EditorPropertyLocalizableString::update_property() {
if (page_index == max_page) {
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Translation"));
button_add_item->set_accessibility_name(TTRC("Add Translation"));
button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocalizableString::_add_locale_popup));
property_vbox->add_child(button_add_item);
@ -1653,6 +1721,7 @@ EditorPropertyLocalizableString::EditorPropertyLocalizableString() {
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
edit = memnew(Button);
edit->set_accessibility_name(TTRC("Edit"));
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->set_clip_text(true);
edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocalizableString::_edit_pressed));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PROPERTIES_ARRAY_DICT_H
#define EDITOR_PROPERTIES_ARRAY_DICT_H
#pragma once
#include "editor/editor_inspector.h"
#include "editor/editor_locale_dialog.h"
@ -54,8 +53,6 @@ public:
void set_array(const Variant &p_array);
Variant get_array();
EditorPropertyArrayObject();
};
class EditorPropertyDictionaryObject : public RefCounted {
@ -89,8 +86,6 @@ public:
String get_label_for_index(int p_index);
String get_property_name_for_index(int p_index);
String get_key_name_for_index(int p_index);
EditorPropertyDictionaryObject();
};
class EditorPropertyArray : public EditorProperty {
@ -164,6 +159,7 @@ protected:
virtual void _remove_pressed(int p_index);
virtual void _button_draw();
virtual void _button_add_item_draw();
virtual bool _is_drop_valid(const Dictionary &p_drag_data) const;
virtual bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
virtual void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
@ -305,5 +301,3 @@ public:
virtual void update_property() override;
EditorPropertyLocalizableString();
};
#endif // EDITOR_PROPERTIES_ARRAY_DICT_H

View file

@ -222,6 +222,7 @@ EditorPropertyVectorN::EditorPropertyVectorN(Variant::Type p_type, bool p_force_
bc->add_child(spin[i]);
spin[i]->set_flat(true);
spin[i]->set_label(String(COMPONENT_LABELS[i]));
spin[i]->set_accessibility_name(String(COMPONENT_LABELS[i]));
if (horizontal) {
spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
}
@ -238,6 +239,7 @@ EditorPropertyVectorN::EditorPropertyVectorN(Variant::Type p_type, bool p_force_
linked->set_toggle_mode(true);
linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
linked->set_tooltip_text(TTR("Lock/Unlock Component Ratio"));
linked->set_accessibility_name(TTRC("Lock/Unlock Component Ratio"));
linked->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyVectorN::_update_ratio));
linked->connect(SceneStringName(toggled), callable_mp(this, &EditorPropertyVectorN::_store_link));
hb->add_child(linked);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PROPERTIES_VECTOR_H
#define EDITOR_PROPERTIES_VECTOR_H
#pragma once
#include "editor/editor_inspector.h"
@ -107,5 +106,3 @@ class EditorPropertyVector4i : public EditorPropertyVectorN {
public:
EditorPropertyVector4i(bool p_force_wide = false);
};
#endif // EDITOR_PROPERTIES_VECTOR_H

View file

@ -59,11 +59,7 @@ EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_tooltip_styl
}
bool EditorPropertyNameProcessor::is_localization_available() {
if (!EditorSettings::get_singleton()) {
return false;
}
const Vector<String> forbidden = String("en").split(",");
return !forbidden.has(EDITOR_GET("interface/editor/editor_language"));
return EditorSettings::get_singleton() && EDITOR_GET("interface/editor/editor_language") != "en";
}
String EditorPropertyNameProcessor::_capitalize_name(const String &p_name) const {
@ -300,6 +296,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["us"] = U"(µs)"; // Unit.
capitalize_string_remaps["usb"] = "USB";
capitalize_string_remaps["usec"] = U"(µsec)"; // Unit.
capitalize_string_remaps["uid"] = "UID";
capitalize_string_remaps["uuid"] = "UUID";
capitalize_string_remaps["uv"] = "UV";
capitalize_string_remaps["uv1"] = "UV1";
@ -314,6 +311,8 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["webrtc"] = "WebRTC";
capitalize_string_remaps["websocket"] = "WebSocket";
capitalize_string_remaps["wine"] = "wine";
capitalize_string_remaps["wintab"] = "WinTab";
capitalize_string_remaps["winink"] = "Windows Ink";
capitalize_string_remaps["wifi"] = "Wi-Fi";
capitalize_string_remaps["x86"] = "x86";
capitalize_string_remaps["x86_32"] = "x86_32";

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PROPERTY_NAME_PROCESSOR_H
#define EDITOR_PROPERTY_NAME_PROCESSOR_H
#pragma once
#include "scene/main/node.h"
@ -76,5 +75,3 @@ public:
EditorPropertyNameProcessor();
~EditorPropertyNameProcessor();
};
#endif // EDITOR_PROPERTY_NAME_PROCESSOR_H

Some files were not shown because too many files have changed in this diff Show more