feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ Variant ActionMapEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from
|
|||
Label *label = memnew(Label(name));
|
||||
label->set_theme_type_variation("HeaderSmall");
|
||||
label->set_modulate(Color(1, 1, 1, 1.0f));
|
||||
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
action_tree->set_drag_preview(label);
|
||||
|
||||
Dictionary drag_data;
|
||||
|
|
@ -357,7 +358,7 @@ void ActionMapEditor::_notification(int p_what) {
|
|||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
action_list_search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
add_button->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
add_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
if (!actions_cache.is_empty()) {
|
||||
update_action_list();
|
||||
}
|
||||
|
|
@ -569,8 +570,9 @@ ActionMapEditor::ActionMapEditor() {
|
|||
add_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
add_edit->set_placeholder(TTR("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));
|
||||
add_edit->connect("text_submitted", callable_mp(this, &ActionMapEditor::_add_action));
|
||||
add_edit->connect(SceneStringName(text_submitted), callable_mp(this, &ActionMapEditor::_add_action));
|
||||
add_hbox->add_child(add_edit);
|
||||
|
||||
add_button = memnew(Button);
|
||||
|
|
@ -584,11 +586,11 @@ ActionMapEditor::ActionMapEditor() {
|
|||
|
||||
show_builtin_actions_checkbutton = memnew(CheckButton);
|
||||
show_builtin_actions_checkbutton->set_text(TTR("Show Built-in Actions"));
|
||||
show_builtin_actions_checkbutton->connect("toggled", callable_mp(this, &ActionMapEditor::set_show_builtin_actions));
|
||||
show_builtin_actions_checkbutton->connect(SceneStringName(toggled), callable_mp(this, &ActionMapEditor::set_show_builtin_actions));
|
||||
add_hbox->add_child(show_builtin_actions_checkbutton);
|
||||
|
||||
show_builtin_actions = EditorSettings::get_singleton()->get_project_metadata("project_settings", "show_builtin_actions", false);
|
||||
show_builtin_actions_checkbutton->set_pressed(show_builtin_actions);
|
||||
show_builtin_actions_checkbutton->set_pressed_no_signal(show_builtin_actions);
|
||||
|
||||
main_vbox->add_child(add_hbox);
|
||||
|
||||
|
|
|
|||
120
engine/editor/add_metadata_dialog.cpp
Normal file
120
engine/editor/add_metadata_dialog.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/**************************************************************************/
|
||||
/* add_metadata_dialog.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "add_metadata_dialog.h"
|
||||
|
||||
#include "editor/themes/editor_scale.h"
|
||||
|
||||
AddMetadataDialog::AddMetadataDialog() {
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
add_child(vbc);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
vbc->add_child(hbc);
|
||||
hbc->add_child(memnew(Label(TTR("Name:"))));
|
||||
|
||||
add_meta_name = memnew(LineEdit);
|
||||
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);
|
||||
|
||||
hbc->add_child(add_meta_type);
|
||||
|
||||
Control *spacing = memnew(Control);
|
||||
vbc->add_child(spacing);
|
||||
spacing->set_custom_minimum_size(Size2(0, 10 * EDSCALE));
|
||||
|
||||
set_ok_button_text(TTR("Add"));
|
||||
register_text_enter(add_meta_name);
|
||||
|
||||
validation_panel = memnew(EditorValidationPanel);
|
||||
vbc->add_child(validation_panel);
|
||||
validation_panel->add_line(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name is valid."));
|
||||
validation_panel->set_update_callback(callable_mp(this, &AddMetadataDialog::_check_meta_name));
|
||||
validation_panel->set_accept_button(get_ok_button());
|
||||
|
||||
add_meta_name->connect(SceneStringName(text_changed), callable_mp(validation_panel, &EditorValidationPanel::update).unbind(1));
|
||||
}
|
||||
|
||||
void AddMetadataDialog::_complete_init(const StringName &p_title) {
|
||||
add_meta_name->set_text("");
|
||||
validation_panel->update();
|
||||
|
||||
set_title(vformat(TTR("Add Metadata Property for \"%s\""), p_title));
|
||||
|
||||
// Skip if we already completed the initialization.
|
||||
if (add_meta_type->get_item_count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Theme icons can be retrieved only the Window has been initialized.
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (i == Variant::NIL || i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) {
|
||||
continue; //not editable by inspector.
|
||||
}
|
||||
String type = i == Variant::OBJECT ? String("Resource") : Variant::get_type_name(Variant::Type(i));
|
||||
|
||||
add_meta_type->add_icon_item(get_editor_theme_icon(type), type, i);
|
||||
}
|
||||
}
|
||||
|
||||
void AddMetadataDialog::open(const StringName p_title, List<StringName> &p_existing_metas) {
|
||||
this->_existing_metas = p_existing_metas;
|
||||
_complete_init(p_title);
|
||||
popup_centered();
|
||||
add_meta_name->grab_focus();
|
||||
}
|
||||
|
||||
StringName AddMetadataDialog::get_meta_name() {
|
||||
return add_meta_name->get_text();
|
||||
}
|
||||
|
||||
Variant AddMetadataDialog::get_meta_defval() {
|
||||
Variant defval;
|
||||
Callable::CallError ce;
|
||||
Variant::construct(Variant::Type(add_meta_type->get_selected_id()), defval, nullptr, 0, ce);
|
||||
return defval;
|
||||
}
|
||||
|
||||
void AddMetadataDialog::_check_meta_name() {
|
||||
const String meta_name = add_meta_name->get_text();
|
||||
|
||||
if (meta_name.is_empty()) {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name can't be empty."), EditorValidationPanel::MSG_ERROR);
|
||||
} else if (!meta_name.is_valid_ascii_identifier()) {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Metadata name must be a valid identifier."), EditorValidationPanel::MSG_ERROR);
|
||||
} else if (_existing_metas.find(meta_name)) {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, vformat(TTR("Metadata with name \"%s\" already exists."), meta_name), EditorValidationPanel::MSG_ERROR);
|
||||
} else if (meta_name[0] == '_') {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Names starting with _ are reserved for editor-only metadata."), EditorValidationPanel::MSG_ERROR);
|
||||
}
|
||||
}
|
||||
59
engine/editor/add_metadata_dialog.h
Normal file
59
engine/editor/add_metadata_dialog.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**************************************************************************/
|
||||
/* add_metadata_dialog.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ADD_METADATA_DIALOG_H
|
||||
#define ADD_METADATA_DIALOG_H
|
||||
|
||||
#include "editor/gui/editor_validation_panel.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
|
||||
class AddMetadataDialog : public ConfirmationDialog {
|
||||
GDCLASS(AddMetadataDialog, ConfirmationDialog);
|
||||
|
||||
public:
|
||||
AddMetadataDialog();
|
||||
void open(const StringName p_title, List<StringName> &p_existing_metas);
|
||||
|
||||
StringName get_meta_name();
|
||||
Variant get_meta_defval();
|
||||
|
||||
private:
|
||||
List<StringName> _existing_metas;
|
||||
|
||||
void _check_meta_name();
|
||||
void _complete_init(const StringName &p_label);
|
||||
|
||||
LineEdit *add_meta_name = nullptr;
|
||||
OptionButton *add_meta_type = nullptr;
|
||||
EditorValidationPanel *validation_panel = nullptr;
|
||||
};
|
||||
#endif // ADD_METADATA_DIALOG_H
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/editor_spin_slider.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/view_panner.h"
|
||||
#include "scene/resources/text_line.h"
|
||||
|
|
@ -54,7 +55,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
|
|||
int limit = timeline->get_name_limit();
|
||||
int right_limit = get_size().width;
|
||||
|
||||
//selection may have altered the order of keys
|
||||
// Selection may have altered the order of keys.
|
||||
RBMap<real_t, int> key_order;
|
||||
|
||||
for (int i = 0; i < animation->track_get_key_count(p_track); i++) {
|
||||
|
|
@ -111,11 +112,11 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
|
|||
int to_x = (offset_n - timeline->get_value()) * scale + limit;
|
||||
int point_end = to_x;
|
||||
|
||||
if (from_x > right_limit) { //not visible
|
||||
if (from_x > right_limit) { // Not visible.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (to_x < limit) { //not visible
|
||||
if (to_x < limit) { // Not visible.
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -132,15 +133,15 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
|
|||
float h;
|
||||
|
||||
if (j == point_end) {
|
||||
h = end.y; //make sure it always connects
|
||||
h = end.y; // Make sure it always connects.
|
||||
} else if (j == point_start) {
|
||||
h = start.y; //make sure it always connects
|
||||
} else { //custom interpolation, used because it needs to show paths affected by moving the selection or handles
|
||||
h = start.y; // Make sure it always connects.
|
||||
} else { // Custom interpolation, used because it needs to show paths affected by moving the selection or handles.
|
||||
int iterations = 10;
|
||||
float low = 0;
|
||||
float high = 1;
|
||||
|
||||
//narrow high and low as much as possible
|
||||
// Narrow high and low as much as possible.
|
||||
for (int k = 0; k < iterations; k++) {
|
||||
float middle = (low + high) / 2.0;
|
||||
|
||||
|
|
@ -153,7 +154,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
|
|||
}
|
||||
}
|
||||
|
||||
//interpolate the result:
|
||||
// Interpolate the result.
|
||||
Vector2 low_pos = start.bezier_interpolate(out_handle, in_handle, end, low);
|
||||
Vector2 high_pos = start.bezier_interpolate(out_handle, in_handle, end, high);
|
||||
|
||||
|
|
@ -174,7 +175,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
|
|||
}
|
||||
|
||||
if (lines.size() >= 2) {
|
||||
draw_multiline(lines, p_color, Math::round(EDSCALE));
|
||||
draw_multiline(lines, p_color, Math::round(EDSCALE), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -208,7 +209,7 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V
|
|||
from = from.lerp(to, c);
|
||||
}
|
||||
|
||||
draw_line(from, to, p_color, Math::round(EDSCALE));
|
||||
draw_line(from, to, p_color, Math::round(EDSCALE), true);
|
||||
}
|
||||
|
||||
void AnimationBezierTrackEdit::_notification(int p_what) {
|
||||
|
|
@ -216,13 +217,14 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
|
||||
if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
|
||||
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
|
||||
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
|
||||
[[fallthrough]];
|
||||
}
|
||||
panner->setup_warped_panning(get_viewport(), EDITOR_GET("editors/panning/warped_mouse_panning"));
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
bezier_icon = get_editor_theme_icon(SNAME("KeyBezierPoint"));
|
||||
bezier_handle_icon = get_editor_theme_icon(SNAME("KeyBezierHandle"));
|
||||
|
|
@ -236,27 +238,29 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
|
||||
int limit = timeline->get_name_limit();
|
||||
|
||||
const Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
|
||||
const int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
|
||||
const Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
|
||||
|
||||
const Color h_line_color = get_theme_color(SNAME("h_line_color"), SNAME("AnimationBezierTrackEdit"));
|
||||
const Color v_line_color = get_theme_color(SNAME("v_line_color"), SNAME("AnimationBezierTrackEdit"));
|
||||
const Color focus_color = get_theme_color(SNAME("focus_color"), SNAME("AnimationBezierTrackEdit"));
|
||||
const Color track_focus_color = get_theme_color(SNAME("track_focus_color"), SNAME("AnimationBezierTrackEdit"));
|
||||
|
||||
const int h_separation = get_theme_constant(SNAME("h_separation"), SNAME("AnimationBezierTrackEdit"));
|
||||
const int v_separation = get_theme_constant(SNAME("h_separation"), SNAME("AnimationBezierTrackEdit"));
|
||||
|
||||
if (has_focus()) {
|
||||
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
accent.a *= 0.7;
|
||||
draw_rect(Rect2(Point2(), get_size()), accent, false, Math::round(EDSCALE));
|
||||
draw_rect(Rect2(Point2(), get_size()), focus_color, false, Math::round(EDSCALE));
|
||||
}
|
||||
|
||||
Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
|
||||
int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
|
||||
Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
|
||||
int hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
|
||||
int vsep = get_theme_constant(SNAME("v_separation"), SNAME("ItemList"));
|
||||
Color linecolor = color;
|
||||
linecolor.a = 0.2;
|
||||
|
||||
draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE));
|
||||
draw_line(Point2(limit, 0), Point2(limit, get_size().height), v_line_color, Math::round(EDSCALE));
|
||||
|
||||
int right_limit = get_size().width;
|
||||
|
||||
track_v_scroll_max = vsep;
|
||||
track_v_scroll_max = v_separation;
|
||||
|
||||
int vofs = vsep + track_v_scroll;
|
||||
int vofs = v_separation + track_v_scroll;
|
||||
int margin = 0;
|
||||
|
||||
RBMap<int, Color> subtrack_colors;
|
||||
|
|
@ -272,7 +276,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
}
|
||||
|
||||
String base_path = animation->track_get_path(i);
|
||||
int end = base_path.find(":");
|
||||
int end = base_path.find_char(':');
|
||||
if (end != -1) {
|
||||
base_path = base_path.substr(0, end + 1);
|
||||
}
|
||||
|
|
@ -286,7 +290,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
|
||||
Vector<int> tracks = E.value;
|
||||
|
||||
// NAMES AND ICON
|
||||
// Names and icon.
|
||||
{
|
||||
NodePath path = animation->track_get_path(tracks[0]);
|
||||
|
||||
|
|
@ -304,15 +308,15 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node");
|
||||
|
||||
text = node->get_name();
|
||||
ofs += hsep;
|
||||
ofs += h_separation;
|
||||
|
||||
TextLine text_buf = TextLine(text, font, font_size);
|
||||
text_buf.set_width(limit - ofs - icon->get_width() - hsep);
|
||||
text_buf.set_width(limit - ofs - icon->get_width() - h_separation);
|
||||
|
||||
int h = MAX(text_buf.get_size().y, icon->get_height());
|
||||
|
||||
draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2.0));
|
||||
ofs += icon->get_width();
|
||||
ofs += icon->get_width() + h_separation;
|
||||
|
||||
margin = icon->get_width();
|
||||
|
||||
|
|
@ -320,31 +324,31 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
string_pos = string_pos.floor();
|
||||
text_buf.draw(get_canvas_item(), string_pos, color);
|
||||
|
||||
vofs += h + vsep;
|
||||
track_v_scroll_max += h + vsep;
|
||||
vofs += h + v_separation;
|
||||
track_v_scroll_max += h + v_separation;
|
||||
}
|
||||
}
|
||||
|
||||
Color dc = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));
|
||||
const Color dc = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));
|
||||
|
||||
Ref<Texture2D> remove = get_editor_theme_icon(SNAME("Remove"));
|
||||
float remove_hpos = limit - hsep - remove->get_width();
|
||||
float remove_hpos = limit - h_separation - remove->get_width();
|
||||
|
||||
Ref<Texture2D> lock = get_editor_theme_icon(SNAME("Lock"));
|
||||
Ref<Texture2D> unlock = get_editor_theme_icon(SNAME("Unlock"));
|
||||
float lock_hpos = remove_hpos - hsep - lock->get_width();
|
||||
float lock_hpos = remove_hpos - h_separation - lock->get_width();
|
||||
|
||||
Ref<Texture2D> visibility_visible = get_editor_theme_icon(SNAME("GuiVisibilityVisible"));
|
||||
Ref<Texture2D> visibility_hidden = get_editor_theme_icon(SNAME("GuiVisibilityHidden"));
|
||||
float visibility_hpos = lock_hpos - hsep - visibility_visible->get_width();
|
||||
float visibility_hpos = lock_hpos - h_separation - visibility_visible->get_width();
|
||||
|
||||
Ref<Texture2D> solo = get_editor_theme_icon(SNAME("AudioBusSolo"));
|
||||
float solo_hpos = visibility_hpos - hsep - solo->get_width();
|
||||
float solo_hpos = visibility_hpos - h_separation - solo->get_width();
|
||||
|
||||
float buttons_width = remove->get_width() + lock->get_width() + visibility_visible->get_width() + solo->get_width() + hsep * 3;
|
||||
float buttons_width = remove->get_width() + lock->get_width() + visibility_visible->get_width() + solo->get_width() + h_separation * 3;
|
||||
|
||||
for (int i = 0; i < tracks.size(); ++i) {
|
||||
// RELATED TRACKS TITLES
|
||||
// Related track titles.
|
||||
|
||||
int current_track = tracks[i];
|
||||
|
||||
|
|
@ -353,9 +357,9 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
|
||||
Color cc = color;
|
||||
TextLine text_buf = TextLine(path, font, font_size);
|
||||
text_buf.set_width(limit - margin - buttons_width);
|
||||
text_buf.set_width(limit - margin - buttons_width - h_separation * 2);
|
||||
|
||||
Rect2 rect = Rect2(margin, vofs, solo_hpos - hsep - solo->get_width(), text_buf.get_size().y + vsep);
|
||||
Rect2 rect = Rect2(margin, vofs, solo_hpos - h_separation - solo->get_width(), text_buf.get_size().y + v_separation);
|
||||
|
||||
cc.a *= 0.7;
|
||||
float h;
|
||||
|
|
@ -381,14 +385,12 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
track_color.set_hsv(h, 0.2, 0.8);
|
||||
}
|
||||
track_color.a = 0.5;
|
||||
draw_rect(Rect2(0, vofs, margin - hsep, text_buf.get_size().y * 0.8), track_color);
|
||||
draw_rect(Rect2(0, vofs, margin - h_separation, text_buf.get_size().y * 0.8), track_color);
|
||||
subtrack_colors[current_track] = track_color;
|
||||
|
||||
subtracks[current_track] = rect;
|
||||
} else {
|
||||
Color ac = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
ac.a = 0.5;
|
||||
draw_rect(rect, ac);
|
||||
draw_rect(rect, track_focus_color);
|
||||
if (locked_tracks.has(selected_track)) {
|
||||
selected_track_color.set_hsv(h, 0.0, 0.4);
|
||||
} else {
|
||||
|
|
@ -396,7 +398,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
Vector2 string_pos = Point2(margin, vofs);
|
||||
Vector2 string_pos = Point2(margin + h_separation, vofs);
|
||||
text_buf.draw(get_canvas_item(), string_pos, cc);
|
||||
|
||||
float icon_start_height = vofs + rect.size.y / 2.0;
|
||||
|
|
@ -432,15 +434,16 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
|
||||
subtrack_icons[current_track] = track_icons;
|
||||
|
||||
vofs += text_buf.get_size().y + vsep;
|
||||
track_v_scroll_max += text_buf.get_size().y + vsep;
|
||||
vofs += text_buf.get_size().y + v_separation;
|
||||
track_v_scroll_max += text_buf.get_size().y + v_separation;
|
||||
}
|
||||
}
|
||||
|
||||
Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
const Color accent = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
|
||||
{ //guides
|
||||
float min_left_scale = font->get_height(font_size) + vsep;
|
||||
// Guides.
|
||||
{
|
||||
float min_left_scale = font->get_height(font_size) + v_separation;
|
||||
|
||||
float scale = (min_left_scale * 2) * timeline_v_zoom;
|
||||
float step = Math::pow(10.0, Math::round(Math::log(scale / 5.0) / Math::log(10.0))) * 5.0;
|
||||
|
|
@ -462,7 +465,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
iv -= 1;
|
||||
}
|
||||
if (!first && iv != prev_iv) {
|
||||
Color lc = linecolor;
|
||||
Color lc = h_line_color;
|
||||
lc.a *= 0.5;
|
||||
draw_line(Point2(limit, i), Point2(right_limit, i), lc, Math::round(EDSCALE));
|
||||
Color c = color;
|
||||
|
|
@ -475,8 +478,8 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
{ //draw OTHER curves
|
||||
|
||||
// Draw other curves.
|
||||
{
|
||||
float scale = timeline->get_zoom_scale();
|
||||
Ref<Texture2D> point = get_editor_theme_icon(SNAME("KeyValue"));
|
||||
for (const KeyValue<int, Color> &E : subtrack_colors) {
|
||||
|
|
@ -498,12 +501,12 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
|
|||
}
|
||||
|
||||
if (track_count > 0 && !hidden_tracks.has(selected_track)) {
|
||||
//draw edited curve
|
||||
// Draw edited curve.
|
||||
_draw_track(selected_track, selected_track_color);
|
||||
}
|
||||
}
|
||||
|
||||
//draw editor handles
|
||||
// Draw editor handles.
|
||||
{
|
||||
edit_points.clear();
|
||||
float scale = timeline->get_zoom_scale();
|
||||
|
|
@ -644,12 +647,12 @@ bool AnimationBezierTrackEdit::_is_track_displayed(int p_track_index) {
|
|||
|
||||
// Check if the curves for a track are displayed in the editor (not hidden). Includes the check on the track visibility.
|
||||
bool AnimationBezierTrackEdit::_is_track_curves_displayed(int p_track_index) {
|
||||
//Is the track is visible in the editor?
|
||||
// Is the track is visible in the editor?
|
||||
if (!_is_track_displayed(p_track_index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//And curves visible?
|
||||
// And curves visible?
|
||||
if (hidden_tracks.has(p_track_index)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -686,7 +689,7 @@ void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) {
|
|||
}
|
||||
|
||||
void AnimationBezierTrackEdit::_play_position_draw() {
|
||||
if (!animation.is_valid() || play_position_pos < 0) {
|
||||
if (animation.is_null() || play_position_pos < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -698,7 +701,7 @@ void AnimationBezierTrackEdit::_play_position_draw() {
|
|||
int px = (-timeline->get_value() + play_position_pos) * scale + limit;
|
||||
|
||||
if (px >= limit && px < (get_size().width)) {
|
||||
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
const Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
||||
play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(2 * EDSCALE));
|
||||
}
|
||||
}
|
||||
|
|
@ -718,7 +721,7 @@ void AnimationBezierTrackEdit::set_root(Node *p_root) {
|
|||
|
||||
void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
|
||||
is_filtered = p_filtered;
|
||||
if (animation == nullptr) {
|
||||
if (animation.is_null()) {
|
||||
return;
|
||||
}
|
||||
String base_path = animation->track_get_path(selected_track);
|
||||
|
|
@ -868,6 +871,11 @@ void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::Hand
|
|||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_out_handle(track_key_pair.first, track_key_pair.second));
|
||||
undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, p_mode, p_auto ? Animation::HANDLE_SET_MODE_AUTO : Animation::HANDLE_SET_MODE_RESET);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -1083,7 +1091,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (I.key == REMOVE_ICON) {
|
||||
if (!read_only) {
|
||||
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
|
||||
undo_redo->create_action("Remove Bezier Track");
|
||||
undo_redo->create_action("Remove Bezier Track", UndoRedo::MERGE_DISABLE, animation.ptr());
|
||||
|
||||
undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
|
||||
undo_redo->add_do_method(this, "_update_hidden_tracks_after", track);
|
||||
|
|
@ -1218,7 +1226,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
//insert new point
|
||||
// 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;
|
||||
Array new_point = animation->make_default_bezier_key(h);
|
||||
|
|
@ -1234,7 +1242,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
|
||||
undo_redo->commit_action();
|
||||
|
||||
//then attempt to move
|
||||
// Then attempt to move.
|
||||
int index = animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX);
|
||||
ERR_FAIL_COND(index == -1);
|
||||
_clear_selection();
|
||||
|
|
@ -1252,7 +1260,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
//box select
|
||||
// Box select.
|
||||
if (mb->get_position().x >= limit && mb->get_position().x < get_size().width) {
|
||||
box_selecting_attempt = true;
|
||||
box_selecting = false;
|
||||
|
|
@ -1264,7 +1272,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
if (box_selecting_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (box_selecting) {
|
||||
//do actual select
|
||||
// Do actual select.
|
||||
if (!box_selecting_add) {
|
||||
_clear_selection();
|
||||
}
|
||||
|
|
@ -1292,13 +1300,13 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
_clear_selection(); //clicked and nothing happened, so clear the selection
|
||||
_clear_selection(); // Clicked and nothing happened, so clear the selection.
|
||||
|
||||
//select by clicking on curve
|
||||
// Select by clicking on curve.
|
||||
int track_count = animation->get_track_count();
|
||||
|
||||
real_t animation_length = animation->get_length();
|
||||
animation->set_length(real_t(INT_MAX)); //bezier_track_interpolate doesn't find keys if they exist beyond anim length
|
||||
animation->set_length(real_t(INT_MAX)); // bezier_track_interpolate doesn't find keys if they exist beyond anim length.
|
||||
|
||||
real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
|
||||
|
||||
|
|
@ -1334,11 +1342,11 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
List<AnimMoveRestore> to_restore;
|
||||
List<Animation::HandleMode> to_restore_handle_modes;
|
||||
// 1-remove the keys
|
||||
// 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
|
||||
// 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) + moving_selection_offset.x;
|
||||
|
||||
|
|
@ -1348,7 +1356,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
if (selection.has(IntPair(E->get().first, idx))) {
|
||||
continue; //already in selection, don't save
|
||||
continue; // Already in selection, don't save.
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
|
||||
|
|
@ -1362,7 +1370,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
to_restore_handle_modes.push_back(animation->bezier_track_get_key_handle_mode(E->get().first, idx));
|
||||
}
|
||||
|
||||
// 3-move the keys (re insert them)
|
||||
// 3 - Move 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) + moving_selection_offset.x;
|
||||
Array key = animation->track_get_key_value(E->get().first, E->get().second);
|
||||
|
|
@ -1381,13 +1389,13 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
|
||||
}
|
||||
|
||||
// 4-(undo) remove inserted keys
|
||||
// 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) + moving_selection_offset.x;
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
|
||||
}
|
||||
|
||||
// 5-(undo) reinsert keys
|
||||
// 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);
|
||||
|
|
@ -1403,7 +1411,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
|
||||
}
|
||||
|
||||
// 6-(undo) reinsert overlapped keys
|
||||
// 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) {
|
||||
|
|
@ -1425,7 +1433,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
|
||||
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
|
||||
|
||||
// 7-reselect
|
||||
// 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);
|
||||
|
|
@ -1436,6 +1444,11 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
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();
|
||||
|
||||
} else if (select_single_attempt != IntPair(-1, -1)) {
|
||||
|
|
@ -1489,11 +1502,6 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
box_selection_to = mm->get_position();
|
||||
|
||||
if (get_local_mouse_position().y < 0) {
|
||||
//avoid cursor from going too above, so it does not lose focus with viewport
|
||||
warp_mouse(Vector2(get_local_mouse_position().x, 0));
|
||||
}
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -1559,6 +1567,11 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio);
|
||||
undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio);
|
||||
}
|
||||
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();
|
||||
moving_handle = 0;
|
||||
queue_redraw();
|
||||
|
|
@ -1638,7 +1651,7 @@ void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_ori
|
|||
Ref<InputEventWithModifiers> iewm = p_event;
|
||||
if (iewm.is_valid() && iewm->is_alt_pressed()) {
|
||||
// Alternate zoom (doesn't affect timeline).
|
||||
timeline_v_zoom = CLAMP(timeline_v_zoom * p_zoom_factor, 0.000001, 100000);
|
||||
timeline_v_zoom = CLAMP(timeline_v_zoom / p_zoom_factor, 0.000001, 100000);
|
||||
} else {
|
||||
float zoom_factor = p_zoom_factor > 1.0 ? AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_IN : AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_OUT;
|
||||
timeline->_zoom_callback(zoom_factor, p_origin, p_event);
|
||||
|
|
@ -1659,7 +1672,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
|
|||
switch (p_index) {
|
||||
case MENU_KEY_INSERT: {
|
||||
if (animation->get_track_count() > 0) {
|
||||
if (editor->snap->is_pressed() && editor->step->get_value() != 0) {
|
||||
if (editor->snap_keys->is_pressed() && editor->step->get_value() != 0) {
|
||||
time = editor->snap_time(time);
|
||||
}
|
||||
while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) {
|
||||
|
|
@ -1672,6 +1685,11 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
|
|||
undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
|
||||
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
|
||||
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
|
||||
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();
|
||||
queue_redraw();
|
||||
}
|
||||
|
|
@ -1735,7 +1753,7 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_
|
|||
real_t insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position();
|
||||
|
||||
if (p_ofs_valid) {
|
||||
if (editor->snap->is_pressed() && editor->step->get_value() != 0) {
|
||||
if (editor->snap_keys->is_pressed() && editor->step->get_value() != 0) {
|
||||
insert_pos = editor->snap_time(insert_pos);
|
||||
}
|
||||
}
|
||||
|
|
@ -1772,6 +1790,11 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_
|
|||
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->add_do_method(this, "queue_redraw");
|
||||
undo_redo->add_undo_method(this, "queue_redraw");
|
||||
undo_redo->commit_action();
|
||||
|
|
@ -1821,6 +1844,15 @@ void AnimationBezierTrackEdit::copy_selected_keys(bool p_cut) {
|
|||
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos, 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->add_do_method(this, "queue_redraw");
|
||||
undo_redo->add_undo_method(this, "queue_redraw");
|
||||
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
|
@ -1858,7 +1890,7 @@ void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) {
|
|||
|
||||
float insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position();
|
||||
if (p_ofs_valid) {
|
||||
if (editor->snap->is_pressed() && editor->step->get_value() != 0) {
|
||||
if (editor->snap_keys->is_pressed() && editor->step->get_value() != 0) {
|
||||
insert_pos = editor->snap_time(insert_pos);
|
||||
}
|
||||
}
|
||||
|
|
@ -1899,9 +1931,15 @@ void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) {
|
|||
i++;
|
||||
}
|
||||
|
||||
undo_redo->commit_action();
|
||||
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->add_do_method(this, "queue_redraw");
|
||||
undo_redo->add_undo_method(this, "queue_redraw");
|
||||
|
||||
queue_redraw();
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1916,6 +1954,11 @@ void AnimationBezierTrackEdit::delete_selection() {
|
|||
}
|
||||
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
|
||||
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
|
||||
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();
|
||||
|
||||
//selection.clear();
|
||||
|
|
@ -1953,9 +1996,9 @@ AnimationBezierTrackEdit::AnimationBezierTrackEdit() {
|
|||
|
||||
set_clip_contents(true);
|
||||
|
||||
ED_SHORTCUT("animation_bezier_editor/focus", TTR("Focus"), Key::F);
|
||||
ED_SHORTCUT("animation_bezier_editor/select_all_keys", TTR("Select All Keys"), KeyModifierMask::CMD_OR_CTRL | Key::A);
|
||||
ED_SHORTCUT("animation_bezier_editor/deselect_all_keys", TTR("Deselect All Keys"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A);
|
||||
ED_SHORTCUT("animation_bezier_editor/focus", TTRC("Focus"), Key::F);
|
||||
ED_SHORTCUT("animation_bezier_editor/select_all_keys", TTRC("Select All Keys"), KeyModifierMask::CMD_OR_CTRL | Key::A);
|
||||
ED_SHORTCUT("animation_bezier_editor/deselect_all_keys", TTRC("Deselect All Keys"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::A);
|
||||
|
||||
menu = memnew(PopupMenu);
|
||||
add_child(menu);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -41,9 +41,11 @@
|
|||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
class AnimationMarkerEdit;
|
||||
class AnimationTrackEditor;
|
||||
class AnimationTrackEdit;
|
||||
class CheckBox;
|
||||
class ColorPickerButton;
|
||||
class EditorSpinSlider;
|
||||
class HSlider;
|
||||
class OptionButton;
|
||||
|
|
@ -52,6 +54,7 @@ class SceneTreeDialog;
|
|||
class SpinBox;
|
||||
class TextureRect;
|
||||
class ViewPanner;
|
||||
class EditorValidationPanel;
|
||||
|
||||
class AnimationTrackKeyEdit : public Object {
|
||||
GDCLASS(AnimationTrackKeyEdit, Object);
|
||||
|
|
@ -128,6 +131,58 @@ protected:
|
|||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
};
|
||||
|
||||
class AnimationMarkerKeyEdit : public Object {
|
||||
GDCLASS(AnimationMarkerKeyEdit, Object);
|
||||
|
||||
public:
|
||||
bool animation_read_only = false;
|
||||
|
||||
Ref<Animation> animation;
|
||||
StringName marker_name;
|
||||
bool use_fps = false;
|
||||
|
||||
AnimationMarkerEdit *marker_edit = nullptr;
|
||||
|
||||
bool _hide_script_from_inspector() { return true; }
|
||||
bool _hide_metadata_from_inspector() { return true; }
|
||||
bool _dont_undo_redo() { return true; }
|
||||
|
||||
bool _is_read_only() { return animation_read_only; }
|
||||
|
||||
float get_time() const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _set_marker_name(const StringName &p_name);
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
};
|
||||
|
||||
class AnimationMultiMarkerKeyEdit : public Object {
|
||||
GDCLASS(AnimationMultiMarkerKeyEdit, Object);
|
||||
|
||||
public:
|
||||
bool animation_read_only = false;
|
||||
|
||||
Ref<Animation> animation;
|
||||
Vector<StringName> marker_names;
|
||||
|
||||
AnimationMarkerEdit *marker_edit = nullptr;
|
||||
|
||||
bool _hide_script_from_inspector() { return true; }
|
||||
bool _hide_metadata_from_inspector() { return true; }
|
||||
bool _dont_undo_redo() { return true; }
|
||||
|
||||
bool _is_read_only() { return animation_read_only; }
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
};
|
||||
|
||||
class AnimationTimelineEdit : public Range {
|
||||
GDCLASS(AnimationTimelineEdit, Range);
|
||||
|
||||
|
|
@ -177,7 +232,7 @@ class AnimationTimelineEdit : public Range {
|
|||
double hscroll_on_zoom_buffer = -1.0;
|
||||
|
||||
Vector2 zoom_scroll_origin;
|
||||
bool zoom_callback_occured = false;
|
||||
bool zoom_callback_occurred = false;
|
||||
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
void _track_added(int p_track);
|
||||
|
|
@ -218,6 +273,140 @@ public:
|
|||
AnimationTimelineEdit();
|
||||
};
|
||||
|
||||
class AnimationMarkerEdit : public Control {
|
||||
GDCLASS(AnimationMarkerEdit, Control);
|
||||
friend class AnimationTimelineEdit;
|
||||
|
||||
enum {
|
||||
MENU_KEY_INSERT,
|
||||
MENU_KEY_RENAME,
|
||||
MENU_KEY_DELETE,
|
||||
MENU_KEY_TOGGLE_MARKER_NAMES,
|
||||
};
|
||||
|
||||
AnimationTimelineEdit *timeline = nullptr;
|
||||
Control *play_position = nullptr; // Separate control used to draw so updates for only position changed are much faster.
|
||||
float play_position_pos = 0.0f;
|
||||
|
||||
HashSet<StringName> selection;
|
||||
|
||||
Ref<Animation> animation;
|
||||
bool read_only = false;
|
||||
|
||||
Ref<Texture2D> type_icon;
|
||||
Ref<Texture2D> selected_icon;
|
||||
|
||||
PopupMenu *menu = nullptr;
|
||||
|
||||
bool hovered = false;
|
||||
StringName hovering_marker;
|
||||
|
||||
void _zoom_changed();
|
||||
|
||||
Ref<Texture2D> icon_cache;
|
||||
|
||||
void _menu_selected(int p_index);
|
||||
|
||||
void _play_position_draw();
|
||||
bool _try_select_at_ui_pos(const Point2 &p_pos, bool p_aggregate, bool p_deselectable);
|
||||
bool _is_ui_pos_in_current_section(const Point2 &p_pos);
|
||||
|
||||
float insert_at_pos = 0.0f;
|
||||
bool moving_selection_attempt = false;
|
||||
bool moving_selection_effective = false;
|
||||
float moving_selection_offset = 0.0f;
|
||||
float moving_selection_pivot = 0.0f;
|
||||
float moving_selection_mouse_begin_x = 0.0f;
|
||||
float moving_selection_mouse_begin_y = 0.0f;
|
||||
StringName select_single_attempt;
|
||||
bool moving_selection = false;
|
||||
void _move_selection_begin();
|
||||
void _move_selection(float p_offset);
|
||||
void _move_selection_commit();
|
||||
void _move_selection_cancel();
|
||||
|
||||
void _clear_selection_for_anim(const Ref<Animation> &p_anim);
|
||||
void _select_key(const StringName &p_name, bool is_single = false);
|
||||
void _deselect_key(const StringName &p_name);
|
||||
|
||||
void _insert_marker(float p_ofs);
|
||||
void _rename_marker(const StringName &p_name);
|
||||
void _delete_selected_markers();
|
||||
|
||||
ConfirmationDialog *marker_insert_confirm = nullptr;
|
||||
LineEdit *marker_insert_new_name = nullptr;
|
||||
ColorPickerButton *marker_insert_color = nullptr;
|
||||
AcceptDialog *marker_insert_error_dialog = nullptr;
|
||||
float marker_insert_ofs = 0;
|
||||
|
||||
ConfirmationDialog *marker_rename_confirm = nullptr;
|
||||
LineEdit *marker_rename_new_name = nullptr;
|
||||
StringName marker_rename_prev_name;
|
||||
|
||||
AcceptDialog *marker_rename_error_dialog = nullptr;
|
||||
|
||||
bool should_show_all_marker_names = false;
|
||||
|
||||
////////////// edit menu stuff
|
||||
|
||||
void _marker_insert_confirmed();
|
||||
void _marker_insert_new_name_changed(const String &p_text);
|
||||
void _marker_rename_confirmed();
|
||||
void _marker_rename_new_name_changed(const String &p_text);
|
||||
|
||||
AnimationTrackEditor *editor = nullptr;
|
||||
|
||||
HBoxContainer *_create_hbox_labeled_control(const String &p_text, Control *p_control) const;
|
||||
|
||||
void _update_key_edit();
|
||||
void _clear_key_edit();
|
||||
|
||||
AnimationMarkerKeyEdit *key_edit = nullptr;
|
||||
AnimationMultiMarkerKeyEdit *multi_key_edit = nullptr;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
virtual String get_tooltip(const Point2 &p_pos) const override;
|
||||
|
||||
virtual int get_key_height() const;
|
||||
virtual Rect2 get_key_rect(float p_pixels_sec) const;
|
||||
virtual bool is_key_selectable_by_distance() const;
|
||||
virtual void draw_key(const StringName &p_name, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right);
|
||||
virtual void draw_bg(int p_clip_left, int p_clip_right);
|
||||
virtual void draw_fg(int p_clip_left, int p_clip_right);
|
||||
|
||||
Ref<Animation> get_animation() const;
|
||||
AnimationTimelineEdit *get_timeline() const { return timeline; }
|
||||
AnimationTrackEditor *get_editor() const { return editor; }
|
||||
bool is_selection_active() const { return !selection.is_empty(); }
|
||||
bool is_moving_selection() const { return moving_selection; }
|
||||
float get_moving_selection_offset() const { return moving_selection_offset; }
|
||||
void set_animation(const Ref<Animation> &p_animation, bool p_read_only);
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
void set_timeline(AnimationTimelineEdit *p_timeline);
|
||||
void set_editor(AnimationTrackEditor *p_editor);
|
||||
|
||||
void set_play_position(float p_pos);
|
||||
void update_play_position();
|
||||
|
||||
void set_use_fps(bool p_use_fps);
|
||||
|
||||
PackedStringArray get_selected_section() const;
|
||||
bool is_marker_selected(const StringName &p_marker) const;
|
||||
|
||||
// For use by AnimationTrackEditor.
|
||||
void _clear_selection(bool p_update);
|
||||
|
||||
AnimationMarkerEdit();
|
||||
~AnimationMarkerEdit();
|
||||
};
|
||||
|
||||
class AnimationTrackEdit : public Control {
|
||||
GDCLASS(AnimationTrackEdit, Control);
|
||||
friend class AnimationTimelineEdit;
|
||||
|
|
@ -367,6 +556,7 @@ class AnimationTrackEditGroup : public Control {
|
|||
NodePath node;
|
||||
Node *root = nullptr;
|
||||
AnimationTimelineEdit *timeline = nullptr;
|
||||
AnimationTrackEditor *editor = nullptr;
|
||||
|
||||
void _zoom_changed();
|
||||
|
||||
|
|
@ -380,6 +570,7 @@ public:
|
|||
virtual Size2 get_minimum_size() const override;
|
||||
void set_timeline(AnimationTimelineEdit *p_timeline);
|
||||
void set_root(Node *p_root);
|
||||
void set_editor(AnimationTrackEditor *p_editor);
|
||||
|
||||
AnimationTrackEditGroup();
|
||||
};
|
||||
|
|
@ -388,6 +579,7 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
GDCLASS(AnimationTrackEditor, VBoxContainer);
|
||||
friend class AnimationTimelineEdit;
|
||||
friend class AnimationBezierTrackEdit;
|
||||
friend class AnimationMarkerKeyEditEditor;
|
||||
|
||||
Ref<Animation> animation;
|
||||
bool read_only = false;
|
||||
|
|
@ -400,14 +592,19 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
ScrollContainer *scroll = nullptr;
|
||||
VBoxContainer *track_vbox = nullptr;
|
||||
AnimationBezierTrackEdit *bezier_edit = nullptr;
|
||||
VBoxContainer *timeline_vbox = nullptr;
|
||||
|
||||
Label *info_message = nullptr;
|
||||
|
||||
AnimationTimelineEdit *timeline = nullptr;
|
||||
AnimationMarkerEdit *marker_edit = nullptr;
|
||||
HSlider *zoom = nullptr;
|
||||
EditorSpinSlider *step = nullptr;
|
||||
Button *fps_compat = nullptr;
|
||||
Label *nearest_fps_label = nullptr;
|
||||
TextureRect *zoom_icon = nullptr;
|
||||
Button *snap = nullptr;
|
||||
Button *snap_keys = nullptr;
|
||||
Button *snap_timeline = nullptr;
|
||||
Button *bezier_edit_icon = nullptr;
|
||||
OptionButton *snap_mode = nullptr;
|
||||
Button *auto_fit = nullptr;
|
||||
|
|
@ -442,6 +639,8 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
void _track_grab_focus(int p_track);
|
||||
|
||||
void _update_scroll(double);
|
||||
void _update_nearest_fps_label();
|
||||
void _update_fps_compat_mode(bool p_enabled);
|
||||
void _update_step(double p_new_step);
|
||||
void _update_length(double p_new_len);
|
||||
void _dropped_track(int p_from_track, int p_to_track);
|
||||
|
|
@ -496,6 +695,10 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
|
||||
PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr);
|
||||
|
||||
void _scroll_changed(const Vector2 &p_val);
|
||||
void _v_scroll_changed(float p_val);
|
||||
void _h_scroll_changed(float p_val);
|
||||
|
||||
Ref<ViewPanner> panner;
|
||||
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
|
||||
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
|
||||
|
|
@ -517,7 +720,7 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
struct SelectedKey {
|
||||
int track = 0;
|
||||
int key = 0;
|
||||
bool operator<(const SelectedKey &p_key) const { return track == p_key.track ? key < p_key.key : track < p_key.track; };
|
||||
bool operator<(const SelectedKey &p_key) const { return track == p_key.track ? key < p_key.key : track < p_key.track; }
|
||||
};
|
||||
|
||||
struct KeyInfo {
|
||||
|
|
@ -538,11 +741,15 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
void _update_key_edit();
|
||||
void _clear_key_edit();
|
||||
|
||||
Control *box_selection_container = nullptr;
|
||||
|
||||
Control *box_selection = nullptr;
|
||||
void _box_selection_draw();
|
||||
bool box_selecting = false;
|
||||
Vector2 box_selecting_from;
|
||||
Vector2 box_selecting_to;
|
||||
Rect2 box_select_rect;
|
||||
Vector2 prev_scroll_position;
|
||||
void _scroll_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
Vector<Ref<AnimationTrackEditPlugin>> track_edit_plugins;
|
||||
|
|
@ -648,9 +855,10 @@ class AnimationTrackEditor : public VBoxContainer {
|
|||
|
||||
void _pick_track_filter_text_changed(const String &p_newtext);
|
||||
void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates);
|
||||
void _pick_track_filter_input(const Ref<InputEvent> &p_ie);
|
||||
|
||||
double snap_unit;
|
||||
bool fps_compatible = true;
|
||||
int nearest_fps = 0;
|
||||
void _update_snap_unit();
|
||||
|
||||
protected:
|
||||
|
|
@ -712,8 +920,8 @@ public:
|
|||
void cleanup();
|
||||
|
||||
void set_anim_pos(float p_pos);
|
||||
void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
|
||||
void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
|
||||
void insert_node_value_key(Node *p_node, const String &p_property, bool p_only_if_exists = false, bool p_advance = false);
|
||||
void insert_value_key(const String &p_property, bool p_advance);
|
||||
void insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant &p_value);
|
||||
bool has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type);
|
||||
void make_insert_queue();
|
||||
|
|
@ -727,11 +935,18 @@ public:
|
|||
bool is_selection_active() const;
|
||||
bool is_key_clipboard_active() const;
|
||||
bool is_moving_selection() const;
|
||||
bool is_snap_enabled() const;
|
||||
bool is_snap_timeline_enabled() const;
|
||||
bool is_snap_keys_enabled() const;
|
||||
bool is_bezier_editor_active() const;
|
||||
bool can_add_reset_key() const;
|
||||
float get_moving_selection_offset() const;
|
||||
float snap_time(float p_value, bool p_relative = false);
|
||||
float get_snap_unit();
|
||||
bool is_grouping_tracks();
|
||||
PackedStringArray get_selected_section() const;
|
||||
bool is_marker_selected(const StringName &p_marker) const;
|
||||
bool is_marker_moving_selection() const;
|
||||
float get_marker_moving_selection_offset() const;
|
||||
|
||||
/** If `p_from_mouse_event` is `true`, handle Shift key presses for precise snapping. */
|
||||
void goto_prev_step(bool p_from_mouse_event);
|
||||
|
|
@ -762,6 +977,7 @@ class AnimationTrackKeyEditEditor : public EditorProperty {
|
|||
Variant value;
|
||||
} key_data_cache;
|
||||
|
||||
void _time_edit_spun();
|
||||
void _time_edit_entered();
|
||||
void _time_edit_exited();
|
||||
|
||||
|
|
@ -770,4 +986,22 @@ public:
|
|||
~AnimationTrackKeyEditEditor();
|
||||
};
|
||||
|
||||
// AnimationMarkerKeyEditEditorPlugin
|
||||
|
||||
class AnimationMarkerKeyEditEditor : public EditorProperty {
|
||||
GDCLASS(AnimationMarkerKeyEditEditor, EditorProperty);
|
||||
|
||||
Ref<Animation> animation;
|
||||
StringName marker_name;
|
||||
bool use_fps = false;
|
||||
|
||||
EditorSpinSlider *spinner = nullptr;
|
||||
|
||||
void _time_edit_exited();
|
||||
|
||||
public:
|
||||
AnimationMarkerKeyEditEditor(Ref<Animation> p_animation, const StringName &p_name, bool p_use_fps);
|
||||
~AnimationMarkerKeyEditEditor();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_TRACK_EDITOR_H
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@
|
|||
#include "scene/2d/sprite_2d.h"
|
||||
#include "scene/3d/sprite_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/resources/text_line.h"
|
||||
#include "servers/audio/audio_stream.h"
|
||||
|
||||
/// BOOL ///
|
||||
|
|
@ -221,7 +220,7 @@ Rect2 AnimationTrackEditAudio::get_key_rect(int p_index, float p_pixels_sec) {
|
|||
|
||||
Ref<AudioStream> stream = object->call("get_stream");
|
||||
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
|
||||
}
|
||||
|
||||
|
|
@ -261,7 +260,7 @@ void AnimationTrackEditAudio::draw_key(int p_index, float p_pixels_sec, int p_x,
|
|||
|
||||
Ref<AudioStream> stream = object->call("get_stream");
|
||||
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
|
||||
return;
|
||||
}
|
||||
|
|
@ -353,9 +352,6 @@ void AnimationTrackEditAudio::set_node(Object *p_object) {
|
|||
id = p_object->get_instance_id();
|
||||
}
|
||||
|
||||
void AnimationTrackEditAudio::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationTrackEditAudio::AnimationTrackEditAudio() {
|
||||
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditAudio::_preview_changed));
|
||||
}
|
||||
|
|
@ -383,7 +379,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
|
|||
|
||||
if (Object::cast_to<Sprite2D>(object) || Object::cast_to<Sprite3D>(object)) {
|
||||
Ref<Texture2D> texture = object->call("get_texture");
|
||||
if (!texture.is_valid()) {
|
||||
if (texture.is_null()) {
|
||||
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
|
||||
}
|
||||
|
||||
|
|
@ -421,12 +417,12 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
|
|||
animation_path = animation_path.replace(":frame", ":animation");
|
||||
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
||||
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
||||
int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
|
||||
animation_name = get_animation()->track_get_key_value(animation_track, animaiton_index);
|
||||
int animation_index = get_animation()->track_find_key(animation_track, track_time);
|
||||
animation_name = get_animation()->track_get_key_value(animation_track, animation_index);
|
||||
}
|
||||
|
||||
Ref<Texture2D> texture = sf->get_frame_texture(animation_name, frame);
|
||||
if (!texture.is_valid()) {
|
||||
if (texture.is_null()) {
|
||||
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +456,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
|
|||
|
||||
if (Object::cast_to<Sprite2D>(object) || Object::cast_to<Sprite3D>(object)) {
|
||||
texture = object->call("get_texture");
|
||||
if (!texture.is_valid()) {
|
||||
if (texture.is_null()) {
|
||||
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
|
||||
return;
|
||||
}
|
||||
|
|
@ -513,12 +509,12 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
|
|||
animation_path = animation_path.replace(":frame", ":animation");
|
||||
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
||||
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
||||
int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
|
||||
animation_name = get_animation()->track_get_key_value(animation_track, animaiton_index);
|
||||
int animation_index = get_animation()->track_find_key(animation_track, track_time);
|
||||
animation_name = get_animation()->track_get_key_value(animation_track, animation_index);
|
||||
}
|
||||
|
||||
texture = sf->get_frame_texture(animation_name, frame);
|
||||
if (!texture.is_valid()) {
|
||||
if (texture.is_null()) {
|
||||
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right);
|
||||
return;
|
||||
}
|
||||
|
|
@ -812,7 +808,7 @@ int AnimationTrackEditTypeAudio::get_key_height() const {
|
|||
Rect2 AnimationTrackEditTypeAudio::get_key_rect(int p_index, float p_pixels_sec) {
|
||||
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);
|
||||
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return AnimationTrackEdit::get_key_rect(p_index, p_pixels_sec);
|
||||
}
|
||||
|
||||
|
|
@ -845,7 +841,7 @@ bool AnimationTrackEditTypeAudio::is_key_selectable_by_distance() const {
|
|||
|
||||
void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
|
||||
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), p_index);
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
AnimationTrackEdit::draw_key(p_index, p_pixels_sec, p_x, p_selected, p_clip_left, p_clip_right); // Draw diamond.
|
||||
return;
|
||||
}
|
||||
|
|
@ -952,9 +948,6 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int
|
|||
}
|
||||
}
|
||||
|
||||
void AnimationTrackEditTypeAudio::_bind_methods() {
|
||||
}
|
||||
|
||||
AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() {
|
||||
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditTypeAudio::_preview_changed));
|
||||
}
|
||||
|
|
@ -1032,7 +1025,7 @@ void AnimationTrackEditTypeAudio::gui_input(const Ref<InputEvent> &p_event) {
|
|||
for (int i = 0; i < get_animation()->track_get_key_count(get_track()); i++) {
|
||||
Ref<AudioStream> stream = get_animation()->audio_track_get_key_stream(get_track(), i);
|
||||
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,6 @@ class AnimationTrackEditAudio : public AnimationTrackEdit {
|
|||
|
||||
void _preview_changed(ObjectID p_which);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual int get_key_height() const override;
|
||||
virtual Rect2 get_key_rect(int p_index, float p_pixels_sec) override;
|
||||
|
|
@ -121,9 +118,6 @@ class AnimationTrackEditTypeAudio : public AnimationTrackEdit {
|
|||
float len_resizing_rel = 0.0f;
|
||||
bool over_drag_position = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,37 +39,72 @@
|
|||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/resources/font.h"
|
||||
|
||||
void GotoLineDialog::popup_find_line(CodeEdit *p_edit) {
|
||||
text_editor = p_edit;
|
||||
void GotoLinePopup::popup_find_line(CodeTextEditor *p_text_editor) {
|
||||
text_editor = p_text_editor;
|
||||
|
||||
// Add 1 because text_editor->get_caret_line() starts from 0, but the editor user interface starts from 1.
|
||||
line->set_text(itos(text_editor->get_caret_line() + 1));
|
||||
line->select_all();
|
||||
popup_centered(Size2(180, 80) * EDSCALE);
|
||||
line->grab_focus();
|
||||
original_state = text_editor->get_navigation_state();
|
||||
|
||||
// Add 1 because the TextEdit starts from 0, but the editor user interface starts from 1.
|
||||
TextEdit *text_edit = text_editor->get_text_editor();
|
||||
int original_line = text_edit->get_caret_line() + 1;
|
||||
line_input->set_text(itos(original_line));
|
||||
text_editor->set_preview_navigation_change(true);
|
||||
|
||||
Rect2i parent_rect = text_editor->get_global_rect();
|
||||
Point2i centered_pos(parent_rect.get_center().x - get_contents_minimum_size().x / 2.0, parent_rect.position.y);
|
||||
popup_on_parent(Rect2i(centered_pos, Size2()));
|
||||
reset_size();
|
||||
line_input->grab_focus();
|
||||
}
|
||||
|
||||
int GotoLineDialog::get_line() const {
|
||||
return line->get_text().to_int();
|
||||
}
|
||||
|
||||
void GotoLineDialog::ok_pressed() {
|
||||
// Subtract 1 because the editor user interface starts from 1, but text_editor->set_caret_line(n) starts from 0.
|
||||
const int line_number = get_line() - 1;
|
||||
if (line_number < 0 || line_number >= text_editor->get_line_count()) {
|
||||
void GotoLinePopup::_goto_line() {
|
||||
if (line_input->get_text().is_empty()) {
|
||||
return;
|
||||
}
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->unfold_line(line_number);
|
||||
text_editor->set_caret_line(line_number);
|
||||
|
||||
PackedStringArray line_col_strings = line_input->get_text().split(":");
|
||||
// Subtract 1 because the editor user interface starts from 1, but the TextEdit starts from 0.
|
||||
const int line_number = line_col_strings[0].to_int() - 1;
|
||||
if (line_number < 0 || line_number >= text_editor->get_text_editor()->get_line_count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int column_number = 0;
|
||||
if (line_col_strings.size() >= 2) {
|
||||
column_number = line_col_strings[1].to_int() - 1;
|
||||
}
|
||||
text_editor->goto_line_centered(line_number, column_number);
|
||||
}
|
||||
|
||||
void GotoLinePopup::_submit() {
|
||||
_goto_line();
|
||||
hide();
|
||||
}
|
||||
|
||||
GotoLineDialog::GotoLineDialog() {
|
||||
void GotoLinePopup::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible()) {
|
||||
text_editor->set_preview_navigation_change(false);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void GotoLinePopup::_input_from_window(const Ref<InputEvent> &p_event) {
|
||||
if (p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
|
||||
// Cancelled, go back to original state.
|
||||
text_editor->set_edit_state(original_state);
|
||||
}
|
||||
PopupPanel::_input_from_window(p_event);
|
||||
}
|
||||
|
||||
GotoLinePopup::GotoLinePopup() {
|
||||
set_title(TTR("Go to Line"));
|
||||
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
|
|
@ -83,14 +118,12 @@ GotoLineDialog::GotoLineDialog() {
|
|||
l->set_text(TTR("Line Number:"));
|
||||
vbc->add_child(l);
|
||||
|
||||
line = memnew(LineEdit);
|
||||
vbc->add_child(line);
|
||||
register_text_enter(line);
|
||||
text_editor = nullptr;
|
||||
|
||||
line_label = nullptr;
|
||||
|
||||
set_hide_on_ok(false);
|
||||
line_input = memnew(LineEdit);
|
||||
line_input->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
|
||||
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));
|
||||
vbc->add_child(line_input);
|
||||
}
|
||||
|
||||
void FindReplaceBar::_notification(int p_what) {
|
||||
|
|
@ -102,16 +135,17 @@ void FindReplaceBar::_notification(int p_what) {
|
|||
[[fallthrough]];
|
||||
}
|
||||
case NOTIFICATION_READY: {
|
||||
find_prev->set_icon(get_editor_theme_icon(SNAME("MoveUp")));
|
||||
find_next->set_icon(get_editor_theme_icon(SNAME("MoveDown")));
|
||||
find_prev->set_button_icon(get_editor_theme_icon(SNAME("MoveUp")));
|
||||
find_next->set_button_icon(get_editor_theme_icon(SNAME("MoveDown")));
|
||||
hide_button->set_texture_normal(get_editor_theme_icon(SNAME("Close")));
|
||||
hide_button->set_texture_hover(get_editor_theme_icon(SNAME("Close")));
|
||||
hide_button->set_texture_pressed(get_editor_theme_icon(SNAME("Close")));
|
||||
hide_button->set_custom_minimum_size(hide_button->get_texture_normal()->get_size());
|
||||
_update_toggle_replace_button(replace_text->is_visible_in_tree());
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
set_process_unhandled_input(is_visible_in_tree());
|
||||
set_process_input(is_visible_in_tree());
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
|
|
@ -127,11 +161,11 @@ void FindReplaceBar::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
|
||||
// Implemented in input(..) as the LineEdit consumes the Escape pressed key.
|
||||
void FindReplaceBar::input(const Ref<InputEvent> &p_event) {
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
Ref<InputEventKey> k = p_event;
|
||||
|
||||
if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
|
||||
Control *focus_owner = get_viewport()->gui_get_focus_owner();
|
||||
|
||||
|
|
@ -142,13 +176,6 @@ void FindReplaceBar::unhandled_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
void FindReplaceBar::_focus_lost() {
|
||||
if (Input::get_singleton()->is_action_pressed(SNAME("ui_cancel"))) {
|
||||
// Unfocused after pressing Escape, so hide the bar.
|
||||
_hide_bar(true);
|
||||
}
|
||||
}
|
||||
|
||||
void FindReplaceBar::_update_flags(bool p_direction_backwards) {
|
||||
flags = 0;
|
||||
|
||||
|
|
@ -175,6 +202,8 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
|
|||
text_editor->unfold_line(pos.y);
|
||||
text_editor->select(pos.y, pos.x, pos.y, pos.x + text.length());
|
||||
text_editor->center_viewport_to_caret(0);
|
||||
text_editor->set_code_hint("");
|
||||
text_editor->cancel_code_completion();
|
||||
|
||||
line_col_changed_for_result = true;
|
||||
}
|
||||
|
|
@ -528,8 +557,8 @@ bool FindReplaceBar::search_next() {
|
|||
return _search(flags, line, col);
|
||||
}
|
||||
|
||||
void FindReplaceBar::_hide_bar(bool p_force_focus) {
|
||||
if (replace_text->has_focus() || search_text->has_focus() || p_force_focus) {
|
||||
void FindReplaceBar::_hide_bar() {
|
||||
if (replace_text->has_focus() || search_text->has_focus()) {
|
||||
text_editor->grab_focus();
|
||||
}
|
||||
|
||||
|
|
@ -539,6 +568,14 @@ void FindReplaceBar::_hide_bar(bool p_force_focus) {
|
|||
hide();
|
||||
}
|
||||
|
||||
void FindReplaceBar::_update_toggle_replace_button(bool p_replace_visible) {
|
||||
String tooltip = p_replace_visible ? TTR("Hide Replace") : TTR("Show Replace");
|
||||
String shortcut = ED_GET_SHORTCUT(p_replace_visible ? "script_text_editor/find" : "script_text_editor/replace")->get_as_text();
|
||||
toggle_replace_button->set_tooltip_text(vformat("%s (%s)", tooltip, shortcut));
|
||||
StringName rtl_compliant_arrow = is_layout_rtl() ? SNAME("GuiTreeArrowLeft") : SNAME("GuiTreeArrowRight");
|
||||
toggle_replace_button->set_button_icon(get_editor_theme_icon(p_replace_visible ? SNAME("GuiTreeArrowDown") : rtl_compliant_arrow));
|
||||
}
|
||||
|
||||
void FindReplaceBar::_show_search(bool p_with_replace, bool p_show_only) {
|
||||
show();
|
||||
if (p_show_only) {
|
||||
|
|
@ -582,6 +619,7 @@ void FindReplaceBar::popup_search(bool p_show_only) {
|
|||
hbc_button_replace->hide();
|
||||
hbc_option_replace->hide();
|
||||
selection_only->set_pressed(false);
|
||||
_update_toggle_replace_button(false);
|
||||
|
||||
_show_search(false, p_show_only);
|
||||
}
|
||||
|
|
@ -591,6 +629,7 @@ void FindReplaceBar::popup_replace() {
|
|||
replace_text->show();
|
||||
hbc_button_replace->show();
|
||||
hbc_option_replace->show();
|
||||
_update_toggle_replace_button(true);
|
||||
}
|
||||
|
||||
selection_only->set_pressed(text_editor->has_selection(0) && text_editor->get_selection_from_line(0) < text_editor->get_selection_to_line(0));
|
||||
|
|
@ -644,6 +683,11 @@ void FindReplaceBar::_replace_text_submitted(const String &p_text) {
|
|||
}
|
||||
}
|
||||
|
||||
void FindReplaceBar::_toggle_replace_pressed() {
|
||||
bool replace_visible = replace_text->is_visible_in_tree();
|
||||
replace_visible ? popup_search(true) : popup_replace();
|
||||
}
|
||||
|
||||
String FindReplaceBar::get_search_text() const {
|
||||
return search_text->get_text();
|
||||
}
|
||||
|
|
@ -702,6 +746,12 @@ void FindReplaceBar::_bind_methods() {
|
|||
}
|
||||
|
||||
FindReplaceBar::FindReplaceBar() {
|
||||
toggle_replace_button = memnew(Button);
|
||||
add_child(toggle_replace_button);
|
||||
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));
|
||||
|
||||
vbc_lineedit = memnew(VBoxContainer);
|
||||
add_child(vbc_lineedit);
|
||||
vbc_lineedit->set_alignment(BoxContainer::ALIGNMENT_CENTER);
|
||||
|
|
@ -725,13 +775,13 @@ FindReplaceBar::FindReplaceBar() {
|
|||
|
||||
// Search toolbar
|
||||
search_text = memnew(LineEdit);
|
||||
search_text->set_keep_editing_on_text_submit(true);
|
||||
vbc_lineedit->add_child(search_text);
|
||||
search_text->set_placeholder(TTR("Find"));
|
||||
search_text->set_tooltip_text(TTR("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("text_submitted", callable_mp(this, &FindReplaceBar::_search_text_submitted));
|
||||
search_text->connect(SceneStringName(focus_exited), callable_mp(this, &FindReplaceBar::_focus_lost));
|
||||
search_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindReplaceBar::_search_text_submitted));
|
||||
|
||||
matches_label = memnew(Label);
|
||||
hbc_button_search->add_child(matches_label);
|
||||
|
|
@ -755,13 +805,13 @@ FindReplaceBar::FindReplaceBar() {
|
|||
hbc_option_search->add_child(case_sensitive);
|
||||
case_sensitive->set_text(TTR("Match Case"));
|
||||
case_sensitive->set_focus_mode(FOCUS_NONE);
|
||||
case_sensitive->connect("toggled", callable_mp(this, &FindReplaceBar::_search_options_changed));
|
||||
case_sensitive->connect(SceneStringName(toggled), callable_mp(this, &FindReplaceBar::_search_options_changed));
|
||||
|
||||
whole_words = memnew(CheckBox);
|
||||
hbc_option_search->add_child(whole_words);
|
||||
whole_words->set_text(TTR("Whole Words"));
|
||||
whole_words->set_focus_mode(FOCUS_NONE);
|
||||
whole_words->connect("toggled", callable_mp(this, &FindReplaceBar::_search_options_changed));
|
||||
whole_words->connect(SceneStringName(toggled), callable_mp(this, &FindReplaceBar::_search_options_changed));
|
||||
|
||||
// Replace toolbar
|
||||
replace_text = memnew(LineEdit);
|
||||
|
|
@ -769,8 +819,7 @@ FindReplaceBar::FindReplaceBar() {
|
|||
replace_text->set_placeholder(TTR("Replace"));
|
||||
replace_text->set_tooltip_text(TTR("Replace"));
|
||||
replace_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
|
||||
replace_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_replace_text_submitted));
|
||||
replace_text->connect(SceneStringName(focus_exited), callable_mp(this, &FindReplaceBar::_focus_lost));
|
||||
replace_text->connect(SceneStringName(text_submitted), callable_mp(this, &FindReplaceBar::_replace_text_submitted));
|
||||
|
||||
replace = memnew(Button);
|
||||
hbc_button_replace->add_child(replace);
|
||||
|
|
@ -786,19 +835,19 @@ FindReplaceBar::FindReplaceBar() {
|
|||
hbc_option_replace->add_child(selection_only);
|
||||
selection_only->set_text(TTR("Selection Only"));
|
||||
selection_only->set_focus_mode(FOCUS_NONE);
|
||||
selection_only->connect("toggled", callable_mp(this, &FindReplaceBar::_search_options_changed));
|
||||
selection_only->connect(SceneStringName(toggled), callable_mp(this, &FindReplaceBar::_search_options_changed));
|
||||
|
||||
hide_button = memnew(TextureButton);
|
||||
add_child(hide_button);
|
||||
hide_button->set_tooltip_text(TTR("Hide"));
|
||||
hide_button->set_focus_mode(FOCUS_NONE);
|
||||
hide_button->connect(SceneStringName(pressed), callable_mp(this, &FindReplaceBar::_hide_bar).bind(false));
|
||||
hide_button->connect(SceneStringName(pressed), callable_mp(this, &FindReplaceBar::_hide_bar));
|
||||
hide_button->set_v_size_flags(SIZE_SHRINK_CENTER);
|
||||
}
|
||||
|
||||
/*** CODE EDITOR ****/
|
||||
|
||||
static constexpr float ZOOM_FACTOR_PRESETS[7] = { 0.25f, 0.5f, 0.75f, 1.0f, 1.5f, 2.0f, 3.0f };
|
||||
static constexpr float ZOOM_FACTOR_PRESETS[8] = { 0.5f, 0.75f, 0.9f, 1.0f, 1.1f, 1.25f, 1.5f, 2.0f };
|
||||
|
||||
// This function should be used to handle shortcuts that could otherwise
|
||||
// be handled too late if they weren't handled here.
|
||||
|
|
@ -807,7 +856,7 @@ void CodeTextEditor::input(const Ref<InputEvent> &event) {
|
|||
|
||||
const Ref<InputEventKey> key_event = event;
|
||||
|
||||
if (!key_event.is_valid()) {
|
||||
if (key_event.is_null()) {
|
||||
return;
|
||||
}
|
||||
if (!key_event->is_pressed()) {
|
||||
|
|
@ -875,12 +924,14 @@ void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef ANDROID_ENABLED
|
||||
Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
|
||||
if (magnify_gesture.is_valid()) {
|
||||
_zoom_to(zoom_factor * powf(magnify_gesture->get_factor(), 0.25f));
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Ref<InputEventKey> k = p_event;
|
||||
|
||||
|
|
@ -994,6 +1045,9 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptLanguage::CodeCo
|
|||
tex = get_editor_theme_icon(p_option.display);
|
||||
} else {
|
||||
tex = EditorNode::get_singleton()->get_class_icon(p_option.display);
|
||||
if (tex.is_null()) {
|
||||
tex = get_editor_theme_icon(SNAME("Object"));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case ScriptLanguage::CODE_COMPLETION_KIND_ENUM:
|
||||
|
|
@ -1006,7 +1060,7 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptLanguage::CodeCo
|
|||
tex = get_editor_theme_icon(SNAME("NodePath"));
|
||||
break;
|
||||
case ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE:
|
||||
tex = get_editor_theme_icon(SNAME("Variant"));
|
||||
tex = get_editor_theme_icon(SNAME("LocalVariable"));
|
||||
break;
|
||||
case ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT:
|
||||
tex = get_editor_theme_icon(SNAME("MemberConstant"));
|
||||
|
|
@ -1065,6 +1119,9 @@ void CodeTextEditor::update_editor_settings() {
|
|||
text_editor->set_draw_spaces(EDITOR_GET("text_editor/appearance/whitespace/draw_spaces"));
|
||||
text_editor->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
|
||||
|
||||
// Behavior: General
|
||||
text_editor->set_empty_selection_clipboard_enabled(EDITOR_GET("text_editor/behavior/general/empty_selection_clipboard"));
|
||||
|
||||
// Behavior: Navigation
|
||||
text_editor->set_scroll_past_end_of_file_enabled(EDITOR_GET("text_editor/behavior/navigation/scroll_past_end_of_file"));
|
||||
text_editor->set_smooth_scroll_enabled(EDITOR_GET("text_editor/behavior/navigation/smooth_scrolling"));
|
||||
|
|
@ -1085,7 +1142,8 @@ void CodeTextEditor::update_editor_settings() {
|
|||
text_editor->set_code_hint_draw_below(EDITOR_GET("text_editor/completion/put_callhint_tooltip_below_current_line"));
|
||||
code_complete_enabled = EDITOR_GET("text_editor/completion/code_complete_enabled");
|
||||
code_complete_timer->set_wait_time(EDITOR_GET("text_editor/completion/code_complete_delay"));
|
||||
idle->set_wait_time(EDITOR_GET("text_editor/completion/idle_parse_delay"));
|
||||
idle_time = EDITOR_GET("text_editor/completion/idle_parse_delay");
|
||||
idle_time_with_errors = EDITOR_GET("text_editor/completion/idle_parse_delay_with_errors_found");
|
||||
|
||||
// Appearance: Guidelines
|
||||
if (EDITOR_GET("text_editor/appearance/guidelines/show_line_length_guidelines")) {
|
||||
|
|
@ -1296,23 +1354,35 @@ void CodeTextEditor::toggle_inline_comment(const String &delimiter) {
|
|||
text_editor->end_complex_operation();
|
||||
}
|
||||
|
||||
void CodeTextEditor::goto_line(int p_line) {
|
||||
void CodeTextEditor::goto_line(int p_line, int p_column) {
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->deselect();
|
||||
text_editor->unfold_line(p_line);
|
||||
callable_mp((TextEdit *)text_editor, &TextEdit::set_caret_line).call_deferred(p_line, true, true, 0, 0);
|
||||
text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
|
||||
text_editor->set_caret_line(p_line, false);
|
||||
text_editor->set_caret_column(p_column, false);
|
||||
text_editor->set_code_hint("");
|
||||
text_editor->cancel_code_completion();
|
||||
// Defer in case the CodeEdit was just created and needs to be resized.
|
||||
callable_mp((TextEdit *)text_editor, &TextEdit::adjust_viewport_to_caret).call_deferred(0);
|
||||
}
|
||||
|
||||
void CodeTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->unfold_line(p_line);
|
||||
callable_mp((TextEdit *)text_editor, &TextEdit::set_caret_line).call_deferred(p_line, true, true, 0, 0);
|
||||
callable_mp((TextEdit *)text_editor, &TextEdit::set_caret_column).call_deferred(p_begin, true, 0);
|
||||
text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
|
||||
text_editor->select(p_line, p_begin, p_line, p_end);
|
||||
text_editor->set_code_hint("");
|
||||
text_editor->cancel_code_completion();
|
||||
callable_mp((TextEdit *)text_editor, &TextEdit::adjust_viewport_to_caret).call_deferred(0);
|
||||
}
|
||||
|
||||
void CodeTextEditor::goto_line_centered(int p_line) {
|
||||
goto_line(p_line);
|
||||
void CodeTextEditor::goto_line_centered(int p_line, int p_column) {
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->deselect();
|
||||
text_editor->unfold_line(CLAMP(p_line, 0, text_editor->get_line_count() - 1));
|
||||
text_editor->set_caret_line(p_line, false);
|
||||
text_editor->set_caret_column(p_column, false);
|
||||
text_editor->set_code_hint("");
|
||||
text_editor->cancel_code_completion();
|
||||
callable_mp((TextEdit *)text_editor, &TextEdit::center_viewport_to_caret).call_deferred(0);
|
||||
}
|
||||
|
||||
|
|
@ -1346,6 +1416,20 @@ void CodeTextEditor::store_previous_state() {
|
|||
previous_state = get_navigation_state();
|
||||
}
|
||||
|
||||
bool CodeTextEditor::is_previewing_navigation_change() const {
|
||||
return preview_navigation_change;
|
||||
}
|
||||
|
||||
void CodeTextEditor::set_preview_navigation_change(bool p_preview) {
|
||||
if (preview_navigation_change == p_preview) {
|
||||
return;
|
||||
}
|
||||
preview_navigation_change = p_preview;
|
||||
if (!preview_navigation_change) {
|
||||
emit_signal("navigation_preview_ended");
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTextEditor::set_edit_state(const Variant &p_state) {
|
||||
Dictionary state = p_state;
|
||||
|
||||
|
|
@ -1440,21 +1524,15 @@ void CodeTextEditor::goto_error() {
|
|||
corrected_column -= tab_count * (indent_size - 1);
|
||||
}
|
||||
|
||||
if (text_editor->get_line_count() != error_line) {
|
||||
text_editor->unfold_line(error_line);
|
||||
}
|
||||
text_editor->remove_secondary_carets();
|
||||
text_editor->set_caret_line(error_line);
|
||||
text_editor->set_caret_column(corrected_column);
|
||||
text_editor->center_viewport_to_caret();
|
||||
goto_line_centered(error_line, corrected_column);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeTextEditor::_update_text_editor_theme() {
|
||||
emit_signal(SNAME("load_theme_settings"));
|
||||
|
||||
error_button->set_icon(get_editor_theme_icon(SNAME("StatusError")));
|
||||
warning_button->set_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError")));
|
||||
warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
|
||||
Ref<Font> status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts));
|
||||
int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts));
|
||||
|
|
@ -1545,7 +1623,8 @@ void CodeTextEditor::_set_show_warnings_panel(bool p_show) {
|
|||
}
|
||||
|
||||
void CodeTextEditor::_toggle_scripts_pressed() {
|
||||
ScriptEditor::get_singleton()->toggle_scripts_panel();
|
||||
ERR_FAIL_NULL(toggle_scripts_list);
|
||||
toggle_scripts_list->set_visible(!toggle_scripts_list->is_visible());
|
||||
update_toggle_scripts_button();
|
||||
}
|
||||
|
||||
|
|
@ -1558,6 +1637,11 @@ void CodeTextEditor::_error_pressed(const Ref<InputEvent> &p_event) {
|
|||
|
||||
void CodeTextEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
set_error_count(0);
|
||||
set_warning_count(0);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
if (toggle_scripts_button->is_visible()) {
|
||||
update_toggle_scripts_button();
|
||||
|
|
@ -1583,8 +1667,11 @@ void CodeTextEditor::_notification(int p_what) {
|
|||
void CodeTextEditor::set_error_count(int p_error_count) {
|
||||
error_button->set_text(itos(p_error_count));
|
||||
error_button->set_visible(p_error_count > 0);
|
||||
if (!p_error_count) {
|
||||
if (p_error_count > 0) {
|
||||
_set_show_errors_panel(false);
|
||||
idle->set_wait_time(idle_time_with_errors); // Parsing should happen sooner.
|
||||
} else {
|
||||
idle->set_wait_time(idle_time);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1690,8 +1777,7 @@ void CodeTextEditor::_zoom_to(float p_zoom_factor) {
|
|||
}
|
||||
|
||||
void CodeTextEditor::set_zoom_factor(float p_zoom_factor) {
|
||||
int preset_count = sizeof(ZOOM_FACTOR_PRESETS) / sizeof(float);
|
||||
zoom_factor = CLAMP(p_zoom_factor, ZOOM_FACTOR_PRESETS[0], ZOOM_FACTOR_PRESETS[preset_count - 1]);
|
||||
zoom_factor = CLAMP(p_zoom_factor, 0.25f, 3.0f);
|
||||
int neutral_font_size = int(EDITOR_GET("interface/editor/code_font_size")) * EDSCALE;
|
||||
int new_font_size = Math::round(zoom_factor * neutral_font_size);
|
||||
|
||||
|
|
@ -1712,6 +1798,7 @@ void CodeTextEditor::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("load_theme_settings"));
|
||||
ADD_SIGNAL(MethodInfo("show_errors_panel"));
|
||||
ADD_SIGNAL(MethodInfo("show_warnings_panel"));
|
||||
ADD_SIGNAL(MethodInfo("navigation_preview_ended"));
|
||||
ADD_SIGNAL(MethodInfo("zoomed", PropertyInfo(Variant::FLOAT, "p_zoom_factor")));
|
||||
}
|
||||
|
||||
|
|
@ -1720,24 +1807,26 @@ void CodeTextEditor::set_code_complete_func(CodeTextEditorCodeCompleteFunc p_cod
|
|||
code_complete_ud = p_ud;
|
||||
}
|
||||
|
||||
void CodeTextEditor::set_toggle_list_control(Control *p_control) {
|
||||
toggle_scripts_list = p_control;
|
||||
}
|
||||
|
||||
void CodeTextEditor::show_toggle_scripts_button() {
|
||||
toggle_scripts_button->show();
|
||||
}
|
||||
|
||||
void CodeTextEditor::update_toggle_scripts_button() {
|
||||
if (is_layout_rtl()) {
|
||||
toggle_scripts_button->set_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back")));
|
||||
} else {
|
||||
toggle_scripts_button->set_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward")));
|
||||
}
|
||||
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()));
|
||||
}
|
||||
|
||||
CodeTextEditor::CodeTextEditor() {
|
||||
code_complete_func = nullptr;
|
||||
ED_SHORTCUT("script_editor/zoom_in", TTR("Zoom In"), KeyModifierMask::CMD_OR_CTRL | Key::EQUAL);
|
||||
ED_SHORTCUT("script_editor/zoom_out", TTR("Zoom Out"), KeyModifierMask::CMD_OR_CTRL | Key::MINUS);
|
||||
ED_SHORTCUT_ARRAY("script_editor/reset_zoom", TTR("Reset Zoom"),
|
||||
ED_SHORTCUT("script_editor/zoom_in", TTRC("Zoom In"), KeyModifierMask::CMD_OR_CTRL | Key::EQUAL);
|
||||
ED_SHORTCUT("script_editor/zoom_out", TTRC("Zoom Out"), KeyModifierMask::CMD_OR_CTRL | Key::MINUS);
|
||||
ED_SHORTCUT_ARRAY("script_editor/reset_zoom", TTRC("Reset Zoom"),
|
||||
{ int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KEY_0), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_0) });
|
||||
|
||||
text_editor = memnew(CodeEdit);
|
||||
|
|
@ -1755,7 +1844,6 @@ CodeTextEditor::CodeTextEditor() {
|
|||
add_child(status_bar);
|
||||
status_bar->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
status_bar->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the warning icon.
|
||||
|
||||
idle = memnew(Timer);
|
||||
add_child(idle);
|
||||
idle->set_one_shot(true);
|
||||
|
|
@ -1796,7 +1884,6 @@ 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"));
|
||||
set_error_count(0);
|
||||
|
||||
// Warnings
|
||||
warning_button = memnew(Button);
|
||||
|
|
@ -1806,7 +1893,6 @@ 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"));
|
||||
set_warning_count(0);
|
||||
|
||||
status_bar->add_child(memnew(VSeparator));
|
||||
|
||||
|
|
@ -1815,7 +1901,10 @@ CodeTextEditor::CodeTextEditor() {
|
|||
status_bar->add_child(zoom_button);
|
||||
zoom_button->set_flat(true);
|
||||
zoom_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER);
|
||||
zoom_button->set_tooltip_text(TTR("Zoom factor"));
|
||||
zoom_button->set_tooltip_text(
|
||||
TTR("Zoom factor") + "\n" +
|
||||
// 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 %");
|
||||
|
||||
PopupMenu *zoom_menu = zoom_button->get_popup();
|
||||
|
|
|
|||
|
|
@ -37,30 +37,33 @@
|
|||
#include "scene/gui/code_edit.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/main/timer.h"
|
||||
|
||||
class MenuButton;
|
||||
class CodeTextEditor;
|
||||
class LineEdit;
|
||||
|
||||
class GotoLineDialog : public ConfirmationDialog {
|
||||
GDCLASS(GotoLineDialog, ConfirmationDialog);
|
||||
class GotoLinePopup : public PopupPanel {
|
||||
GDCLASS(GotoLinePopup, PopupPanel);
|
||||
|
||||
Label *line_label = nullptr;
|
||||
LineEdit *line = nullptr;
|
||||
Variant original_state;
|
||||
LineEdit *line_input = nullptr;
|
||||
CodeTextEditor *text_editor = nullptr;
|
||||
|
||||
CodeEdit *text_editor = nullptr;
|
||||
void _goto_line();
|
||||
void _submit();
|
||||
|
||||
virtual void ok_pressed() override;
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
void popup_find_line(CodeEdit *p_edit);
|
||||
int get_line() const;
|
||||
void popup_find_line(CodeTextEditor *p_text_editor);
|
||||
|
||||
GotoLineDialog();
|
||||
GotoLinePopup();
|
||||
};
|
||||
|
||||
class CodeTextEditor;
|
||||
|
||||
class FindReplaceBar : public HBoxContainer {
|
||||
GDCLASS(FindReplaceBar, HBoxContainer);
|
||||
|
||||
|
|
@ -70,6 +73,7 @@ class FindReplaceBar : public HBoxContainer {
|
|||
SEARCH_PREV,
|
||||
};
|
||||
|
||||
Button *toggle_replace_button = nullptr;
|
||||
LineEdit *search_text = nullptr;
|
||||
Label *matches_label = nullptr;
|
||||
Button *find_prev = nullptr;
|
||||
|
|
@ -100,23 +104,25 @@ class FindReplaceBar : public HBoxContainer {
|
|||
bool replace_all_mode = false;
|
||||
bool preserve_cursor = false;
|
||||
|
||||
virtual void input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
void _get_search_from(int &r_line, int &r_col, SearchMode p_search_mode);
|
||||
void _update_results_count();
|
||||
void _update_matches_display();
|
||||
|
||||
void _show_search(bool p_with_replace, bool p_show_only);
|
||||
void _hide_bar(bool p_force_focus = false);
|
||||
void _hide_bar();
|
||||
void _update_toggle_replace_button(bool p_replace_visible);
|
||||
|
||||
void _editor_text_changed();
|
||||
void _search_options_changed(bool p_pressed);
|
||||
void _search_text_changed(const String &p_text);
|
||||
void _search_text_submitted(const String &p_text);
|
||||
void _replace_text_submitted(const String &p_text);
|
||||
void _toggle_replace_pressed();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
|
||||
void _focus_lost();
|
||||
|
||||
void _update_flags(bool p_direction_backwards);
|
||||
|
||||
|
|
@ -161,6 +167,7 @@ class CodeTextEditor : public VBoxContainer {
|
|||
HBoxContainer *status_bar = nullptr;
|
||||
|
||||
Button *toggle_scripts_button = nullptr;
|
||||
Control *toggle_scripts_list = nullptr;
|
||||
Button *error_button = nullptr;
|
||||
Button *warning_button = nullptr;
|
||||
|
||||
|
|
@ -170,6 +177,8 @@ class CodeTextEditor : public VBoxContainer {
|
|||
|
||||
Label *info = nullptr;
|
||||
Timer *idle = nullptr;
|
||||
float idle_time = 0.0f;
|
||||
float idle_time_with_errors = 0.0f;
|
||||
bool code_complete_enabled = true;
|
||||
Timer *code_complete_timer = nullptr;
|
||||
int code_complete_timer_line = 0;
|
||||
|
|
@ -180,6 +189,7 @@ class CodeTextEditor : public VBoxContainer {
|
|||
int error_line;
|
||||
int error_column;
|
||||
|
||||
bool preview_navigation_change = false;
|
||||
Dictionary previous_state;
|
||||
|
||||
void _update_text_editor_theme();
|
||||
|
|
@ -246,9 +256,9 @@ public:
|
|||
/// by adding or removing comment delimiter
|
||||
void toggle_inline_comment(const String &delimiter);
|
||||
|
||||
void goto_line(int p_line);
|
||||
void goto_line(int p_line, int p_column = 0);
|
||||
void goto_line_selection(int p_line, int p_begin, int p_end);
|
||||
void goto_line_centered(int p_line);
|
||||
void goto_line_centered(int p_line, int p_column = 0);
|
||||
void set_executing_line(int p_line);
|
||||
void clear_executing_line();
|
||||
|
||||
|
|
@ -258,6 +268,9 @@ public:
|
|||
Variant get_previous_state();
|
||||
void store_previous_state();
|
||||
|
||||
bool is_previewing_navigation_change() const;
|
||||
void set_preview_navigation_change(bool p_preview);
|
||||
|
||||
void set_error_count(int p_error_count);
|
||||
void set_warning_count(int p_warning_count);
|
||||
|
||||
|
|
@ -285,6 +298,7 @@ public:
|
|||
|
||||
void validate_script();
|
||||
|
||||
void set_toggle_list_control(Control *p_control);
|
||||
void show_toggle_scripts_button();
|
||||
void update_toggle_scripts_button();
|
||||
|
||||
|
|
|
|||
|
|
@ -34,15 +34,16 @@
|
|||
#include "core/templates/hash_set.h"
|
||||
#include "editor/editor_help.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/gui/scene_tree_editor.h"
|
||||
#include "editor/node_dock.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "plugins/script_editor_plugin.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
|
@ -486,13 +487,8 @@ void ConnectDialog::_notification(int p_what) {
|
|||
type_list->set_item_icon(i, get_editor_theme_icon(type_name));
|
||||
}
|
||||
|
||||
Ref<StyleBox> style = get_theme_stylebox(CoreStringName(normal), "LineEdit")->duplicate();
|
||||
if (style.is_valid()) {
|
||||
style->set_content_margin(SIDE_TOP, style->get_content_margin(SIDE_TOP) + 1.0);
|
||||
from_signal->add_theme_style_override(CoreStringName(normal), style);
|
||||
}
|
||||
method_search->set_right_icon(get_editor_theme_icon("Search"));
|
||||
open_method_tree->set_icon(get_editor_theme_icon("Edit"));
|
||||
open_method_tree->set_button_icon(get_editor_theme_icon("Edit"));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -527,8 +523,8 @@ void ConnectDialog::set_dst_node(Node *p_node) {
|
|||
|
||||
StringName ConnectDialog::get_dst_method_name() const {
|
||||
String txt = dst_method->get_text();
|
||||
if (txt.contains("(")) {
|
||||
txt = txt.left(txt.find("(")).strip_edges();
|
||||
if (txt.contains_char('(')) {
|
||||
txt = txt.left(txt.find_char('(')).strip_edges();
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
|
@ -576,6 +572,22 @@ String ConnectDialog::get_signature(const MethodInfo &p_method, PackedStringArra
|
|||
type_name = "Array";
|
||||
}
|
||||
break;
|
||||
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);
|
||||
if (key_hint.is_empty() || key_hint.begins_with("res://")) {
|
||||
key_hint = "Variant";
|
||||
}
|
||||
if (value_hint.is_empty() || value_hint.begins_with("res://")) {
|
||||
value_hint = "Variant";
|
||||
}
|
||||
if (key_hint != "Variant" || value_hint != "Variant") {
|
||||
type_name += "[" + key_hint + ", " + value_hint + "]";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Variant::OBJECT:
|
||||
if (pi.class_name != StringName()) {
|
||||
type_name = pi.class_name;
|
||||
|
|
@ -723,6 +735,7 @@ ConnectDialog::ConnectDialog() {
|
|||
from_signal->set_editable(false);
|
||||
|
||||
tree = memnew(SceneTreeEditor(false));
|
||||
tree->set_update_when_invisible(false);
|
||||
tree->set_connecting_signal(true);
|
||||
tree->set_show_enabled_subscene(true);
|
||||
tree->set_v_size_flags(Control::SIZE_FILL | Control::SIZE_EXPAND);
|
||||
|
|
@ -895,13 +908,11 @@ ConnectDialog::~ConnectDialog() {
|
|||
|
||||
Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const {
|
||||
// If it's not a doc tooltip, fallback to the default one.
|
||||
if (p_text.contains("::")) {
|
||||
if (p_text.is_empty() || p_text.contains("::")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EditorHelpBit *help_bit = memnew(EditorHelpBit(p_text));
|
||||
EditorHelpBitTooltip::show_tooltip(help_bit, const_cast<ConnectionsDockTree *>(this));
|
||||
return memnew(Control); // Make the standard tooltip invisible.
|
||||
return EditorHelpBitTooltip::show_tooltip(const_cast<ConnectionsDockTree *>(this), p_text);
|
||||
}
|
||||
|
||||
struct _ConnectionsDockMethodInfoSort {
|
||||
|
|
@ -1067,17 +1078,17 @@ void ConnectionsDock::_tree_item_selected() {
|
|||
TreeItem *item = tree->get_selected();
|
||||
if (item && _get_item_type(*item) == TREE_ITEM_TYPE_SIGNAL) {
|
||||
connect_button->set_text(TTR("Connect..."));
|
||||
connect_button->set_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||
connect_button->set_button_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||
connect_button->set_disabled(false);
|
||||
} else if (item && _get_item_type(*item) == TREE_ITEM_TYPE_CONNECTION) {
|
||||
connect_button->set_text(TTR("Disconnect"));
|
||||
connect_button->set_icon(get_editor_theme_icon(SNAME("Unlinked")));
|
||||
connect_button->set_button_icon(get_editor_theme_icon(SNAME("Unlinked")));
|
||||
|
||||
Object::Connection connection = item->get_metadata(0);
|
||||
connect_button->set_disabled(_is_connection_inherited(connection));
|
||||
} else {
|
||||
connect_button->set_text(TTR("Connect..."));
|
||||
connect_button->set_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||
connect_button->set_button_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||
connect_button->set_disabled(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -1180,7 +1191,7 @@ void ConnectionsDock::_go_to_method(TreeItem &p_item) {
|
|||
}
|
||||
|
||||
if (scr.is_valid() && ScriptEditor::get_singleton()->script_goto_method(scr, cd.method)) {
|
||||
EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT);
|
||||
EditorNode::get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1188,7 +1199,7 @@ void ConnectionsDock::_handle_class_menu_option(int p_option) {
|
|||
switch (p_option) {
|
||||
case CLASS_MENU_OPEN_DOCS:
|
||||
ScriptEditor::get_singleton()->goto_help("class:" + class_menu_doc_class_name);
|
||||
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1218,7 +1229,7 @@ void ConnectionsDock::_handle_signal_menu_option(int p_option) {
|
|||
} break;
|
||||
case SIGNAL_MENU_OPEN_DOCS: {
|
||||
ScriptEditor::get_singleton()->goto_help("class_signal:" + String(meta["class"]) + ":" + String(meta["name"]));
|
||||
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1435,7 +1446,7 @@ void ConnectionsDock::update_tree() {
|
|||
doc_class_name = String();
|
||||
}
|
||||
|
||||
class_icon = editor_data.get_script_icon(script_base);
|
||||
class_icon = editor_data.get_script_icon(script_base->get_path());
|
||||
if (class_icon.is_null() && has_theme_icon(native_base, EditorStringName(EditorIcons))) {
|
||||
class_icon = get_editor_theme_icon(native_base);
|
||||
}
|
||||
|
|
@ -1574,7 +1585,7 @@ void ConnectionsDock::update_tree() {
|
|||
}
|
||||
|
||||
connect_button->set_text(TTR("Connect..."));
|
||||
connect_button->set_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||
connect_button->set_button_icon(get_editor_theme_icon(SNAME("Instance")));
|
||||
connect_button->set_disabled(true);
|
||||
}
|
||||
|
||||
|
|
@ -1637,7 +1648,7 @@ ConnectionsDock::ConnectionsDock() {
|
|||
slot_menu->connect("about_to_popup", callable_mp(this, &ConnectionsDock::_slot_menu_about_to_popup));
|
||||
slot_menu->add_item(TTR("Edit..."), SLOT_MENU_EDIT);
|
||||
slot_menu->add_item(TTR("Go to Method"), SLOT_MENU_GO_TO_METHOD);
|
||||
slot_menu->add_shortcut(ED_SHORTCUT("connections_editor/disconnect", TTR("Disconnect"), Key::KEY_DELETE), SLOT_MENU_DISCONNECT);
|
||||
slot_menu->add_shortcut(ED_SHORTCUT("connections_editor/disconnect", TTRC("Disconnect"), Key::KEY_DELETE), SLOT_MENU_DISCONNECT);
|
||||
add_child(slot_menu);
|
||||
|
||||
connect_dialog->connect("connected", callable_mp(this, &ConnectionsDock::_make_or_edit_connection));
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include "create_dialog.h"
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_feature_profile.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
|
|
@ -112,10 +111,28 @@ bool CreateDialog::_is_type_preferred(const String &p_type) const {
|
|||
return EditorNode::get_editor_data().script_class_is_parent(p_type, preferred_search_result_type);
|
||||
}
|
||||
|
||||
void CreateDialog::_script_button_clicked(TreeItem *p_item, int p_column, int p_button_id, MouseButton p_mouse_button_index) {
|
||||
if (p_mouse_button_index != MouseButton::LEFT) {
|
||||
return;
|
||||
}
|
||||
// The id of opening-script button is 1.
|
||||
if (p_button_id != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
String scr_path = ScriptServer::get_global_class_path(p_item->get_text(0));
|
||||
Ref<Script> scr = ResourceLoader::load(scr_path, "Script");
|
||||
ERR_FAIL_COND_MSG(scr.is_null(), vformat("Could not load the script from resource path: %s", scr_path));
|
||||
EditorNode::get_singleton()->push_item_no_inspector(scr.ptr());
|
||||
|
||||
hide();
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
bool CreateDialog::_is_class_disabled_by_feature_profile(const StringName &p_class) const {
|
||||
Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
|
||||
|
||||
return !profile.is_null() && profile->is_class_disabled(p_class);
|
||||
return profile.is_valid() && profile->is_class_disabled(p_class);
|
||||
}
|
||||
|
||||
bool CreateDialog::_should_hide_type(const StringName &p_type) const {
|
||||
|
|
@ -145,6 +162,11 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
|
|||
return true; // Parent type is blacklisted.
|
||||
}
|
||||
}
|
||||
for (const StringName &F : custom_type_blocklist) {
|
||||
if (ClassDB::is_parent_class(p_type, F)) {
|
||||
return true; // Parent type is excluded in custom type blocklist.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!ScriptServer::is_global_class(p_type)) {
|
||||
return true;
|
||||
|
|
@ -154,19 +176,23 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
|
|||
}
|
||||
|
||||
StringName native_type = ScriptServer::get_global_class_native_base(p_type);
|
||||
if (ClassDB::class_exists(native_type) && !ClassDB::can_instantiate(native_type)) {
|
||||
return true;
|
||||
if (ClassDB::class_exists(native_type)) {
|
||||
if (!ClassDB::can_instantiate(native_type)) {
|
||||
return true;
|
||||
} else if (custom_type_blocklist.has(p_type) || custom_type_blocklist.has(native_type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
String script_path = ScriptServer::get_global_class_path(p_type);
|
||||
if (script_path.begins_with("res://addons/")) {
|
||||
int i = script_path.find("/", 13); // 13 is length of "res://addons/".
|
||||
int i = script_path.find_char('/', 13); // 13 is length of "res://addons/".
|
||||
while (i > -1) {
|
||||
const String plugin_path = script_path.substr(0, i).path_join("plugin.cfg");
|
||||
if (FileAccess::exists(plugin_path)) {
|
||||
return !EditorNode::get_singleton()->is_addon_plugin_enabled(plugin_path);
|
||||
}
|
||||
i = script_path.find("/", i + 1);
|
||||
i = script_path.find_char('/', i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -231,14 +257,9 @@ void CreateDialog::_add_type(const StringName &p_type, TypeCategory p_type_categ
|
|||
inherits = ClassDB::get_parent_class(p_type);
|
||||
inherited_type = TypeCategory::CPP_TYPE;
|
||||
} else {
|
||||
if (p_type_category == TypeCategory::PATH_TYPE || ScriptServer::is_global_class(p_type)) {
|
||||
Ref<Script> scr;
|
||||
if (p_type_category == TypeCategory::PATH_TYPE) {
|
||||
ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script"));
|
||||
scr = ResourceLoader::load(p_type, "Script");
|
||||
} else {
|
||||
scr = EditorNode::get_editor_data().script_class_load_script(p_type);
|
||||
}
|
||||
if (p_type_category == TypeCategory::PATH_TYPE) {
|
||||
ERR_FAIL_COND(!ResourceLoader::exists(p_type, "Script"));
|
||||
Ref<Script> scr = ResourceLoader::load(p_type, "Script");
|
||||
ERR_FAIL_COND(scr.is_null());
|
||||
|
||||
Ref<Script> base = scr->get_base_script();
|
||||
|
|
@ -260,6 +281,10 @@ void CreateDialog::_add_type(const StringName &p_type, TypeCategory p_type_categ
|
|||
inherited_type = TypeCategory::PATH_TYPE;
|
||||
}
|
||||
}
|
||||
} else if (ScriptServer::is_global_class(p_type)) {
|
||||
inherits = ScriptServer::get_global_class_base(p_type);
|
||||
bool is_native_class = ClassDB::class_exists(inherits);
|
||||
inherited_type = is_native_class ? TypeCategory::CPP_TYPE : TypeCategory::OTHER_TYPE;
|
||||
} else {
|
||||
inherits = custom_type_parents[p_type];
|
||||
if (ClassDB::class_exists(inherits)) {
|
||||
|
|
@ -288,12 +313,19 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const StringN
|
|||
} else if (script_type) {
|
||||
r_item->set_metadata(0, p_type);
|
||||
r_item->set_text(0, p_type);
|
||||
String script_path = ScriptServer::get_global_class_path(p_type);
|
||||
r_item->set_suffix(0, "(" + script_path.get_file() + ")");
|
||||
|
||||
Ref<Script> scr = ResourceLoader::load(script_path, "Script");
|
||||
ERR_FAIL_COND(!scr.is_valid());
|
||||
is_abstract = scr->is_abstract();
|
||||
is_abstract = ScriptServer::is_global_class_abstract(p_type);
|
||||
|
||||
String tooltip = TTR("Script path: %s");
|
||||
bool is_tool = ScriptServer::is_global_class_tool(p_type);
|
||||
if (is_tool) {
|
||||
tooltip = TTR("The script will run in the editor.") + "\n" + tooltip;
|
||||
}
|
||||
r_item->add_button(0, get_editor_theme_icon(SNAME("Script")), 1, false, vformat(tooltip, ScriptServer::get_global_class_path(p_type)));
|
||||
if (is_tool) {
|
||||
int button_index = r_item->get_button_count(0) - 1;
|
||||
r_item->set_button_color(0, button_index, get_theme_color(SNAME("accent_color"), EditorStringName(Editor)));
|
||||
}
|
||||
} else {
|
||||
r_item->set_metadata(0, custom_type_parents[p_type]);
|
||||
r_item->set_text(0, p_type);
|
||||
|
|
@ -425,26 +457,19 @@ void CreateDialog::_text_changed(const String &p_newtext) {
|
|||
_update_search();
|
||||
}
|
||||
|
||||
void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) {
|
||||
Ref<InputEventKey> k = p_ie;
|
||||
if (k.is_valid() && k->is_pressed()) {
|
||||
switch (k->get_keycode()) {
|
||||
case Key::UP:
|
||||
case Key::DOWN:
|
||||
case Key::PAGEUP:
|
||||
case Key::PAGEDOWN: {
|
||||
search_options->gui_input(k);
|
||||
search_box->accept_event();
|
||||
} break;
|
||||
case Key::SPACE: {
|
||||
TreeItem *ti = search_options->get_selected();
|
||||
if (ti) {
|
||||
ti->set_collapsed(!ti->is_collapsed());
|
||||
}
|
||||
search_box->accept_event();
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
void CreateDialog::_sbox_input(const Ref<InputEvent> &p_event) {
|
||||
// Redirect navigational key events to the tree.
|
||||
Ref<InputEventKey> key = p_event;
|
||||
if (key.is_valid()) {
|
||||
if (key->is_action("ui_up", true) || key->is_action("ui_down", true) || key->is_action("ui_page_up") || key->is_action("ui_page_down")) {
|
||||
search_options->gui_input(key);
|
||||
search_box->accept_event();
|
||||
} else if (key->is_action_pressed("ui_select", true)) {
|
||||
TreeItem *ti = search_options->get_selected();
|
||||
if (ti) {
|
||||
ti->set_collapsed(!ti->is_collapsed());
|
||||
}
|
||||
search_box->accept_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -475,7 +500,7 @@ void CreateDialog::_notification(int p_what) {
|
|||
recent->set_fixed_icon_size(Size2(icon_width, icon_width));
|
||||
|
||||
search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
favorite->set_icon(get_editor_theme_icon(SNAME("Favorites")));
|
||||
favorite->set_button_icon(get_editor_theme_icon(SNAME("Favorites")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -620,8 +645,9 @@ Variant CreateDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
|
|||
|
||||
Button *tb = memnew(Button);
|
||||
tb->set_flat(true);
|
||||
tb->set_icon(ti->get_icon(0));
|
||||
tb->set_button_icon(ti->get_icon(0));
|
||||
tb->set_text(ti->get_text(0));
|
||||
tb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
favorites->set_drag_preview(tb);
|
||||
|
||||
return d;
|
||||
|
|
@ -770,6 +796,7 @@ CreateDialog::CreateDialog() {
|
|||
favorites->connect("cell_selected", callable_mp(this, &CreateDialog::_favorite_selected));
|
||||
favorites->connect("item_activated", callable_mp(this, &CreateDialog::_favorite_activated));
|
||||
favorites->add_theme_constant_override("draw_guides", 1);
|
||||
favorites->set_theme_type_variation("TreeSecondary");
|
||||
SET_DRAG_FORWARDING_GCD(favorites, CreateDialog);
|
||||
fav_vb->add_margin_child(TTR("Favorites:"), favorites, true);
|
||||
|
||||
|
|
@ -785,6 +812,7 @@ CreateDialog::CreateDialog() {
|
|||
recent->connect(SceneStringName(item_selected), callable_mp(this, &CreateDialog::_history_selected));
|
||||
recent->connect("item_activated", callable_mp(this, &CreateDialog::_history_activated));
|
||||
recent->add_theme_constant_override("draw_guides", 1);
|
||||
recent->set_theme_type_variation("ItemListSecondary");
|
||||
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
vbc->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
|
||||
|
|
@ -811,10 +839,11 @@ CreateDialog::CreateDialog() {
|
|||
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));
|
||||
search_options->connect("button_clicked", callable_mp(this, &CreateDialog::_script_button_clicked));
|
||||
vbc->add_margin_child(TTR("Matches:"), search_options, true);
|
||||
|
||||
help_bit = memnew(EditorHelpBit);
|
||||
help_bit->set_content_height_limits(64 * EDSCALE, 64 * EDSCALE);
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class CreateDialog : public ConfirmationDialog {
|
|||
HashMap<String, int> custom_type_indices;
|
||||
List<StringName> type_list;
|
||||
HashSet<StringName> type_blacklist;
|
||||
HashSet<StringName> custom_type_blocklist;
|
||||
|
||||
void _update_search();
|
||||
bool _should_hide_type(const StringName &p_type) const;
|
||||
|
|
@ -73,11 +74,12 @@ class CreateDialog : public ConfirmationDialog {
|
|||
void _configure_search_option_item(TreeItem *r_item, const StringName &p_type, TypeCategory p_type_category);
|
||||
float _score_type(const String &p_type, const String &p_search) const;
|
||||
bool _is_type_preferred(const String &p_type) const;
|
||||
void _script_button_clicked(TreeItem *p_item, int p_column, int p_button_id, MouseButton p_mouse_button_index);
|
||||
|
||||
void _fill_type_list();
|
||||
void _cleanup();
|
||||
|
||||
void _sbox_input(const Ref<InputEvent> &p_ie);
|
||||
void _sbox_input(const Ref<InputEvent> &p_event);
|
||||
void _text_changed(const String &p_newtext);
|
||||
void select_type(const String &p_type, bool p_center_on_item = true);
|
||||
void _item_selected();
|
||||
|
|
@ -115,8 +117,8 @@ public:
|
|||
String get_base_type() const { return base_type; }
|
||||
void select_base();
|
||||
|
||||
void set_type_blocklist(const HashSet<StringName> &p_blocklist) { custom_type_blocklist = p_blocklist; }
|
||||
void set_preferred_search_result_type(const String &p_preferred_type) { preferred_search_result_type = p_preferred_type; }
|
||||
String get_preferred_search_result_type() { return preferred_search_result_type; }
|
||||
|
||||
void popup_create(bool p_dont_clear, bool p_replace_mode = false, const String &p_current_type = "", const String &p_current_name = "");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "debug_adapter_parser.h"
|
||||
|
||||
#include "editor/debugger/debug_adapter/debug_adapter_types.h"
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
|
@ -146,7 +147,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
|
|||
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
|
||||
String breakpoint = E->get();
|
||||
|
||||
String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://"
|
||||
String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://"
|
||||
int line = breakpoint.substr(path.size()).to_int();
|
||||
|
||||
DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true);
|
||||
|
|
@ -358,7 +359,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("\\")) {
|
||||
if (source.path.contains_char('\\')) {
|
||||
source.path = source.path.replace("\\", "/");
|
||||
source.path = source.path.substr(0, 1).to_upper() + source.path.substr(1);
|
||||
}
|
||||
|
|
@ -442,26 +443,34 @@ Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const {
|
|||
return Dictionary();
|
||||
}
|
||||
|
||||
Dictionary response = prepare_success_response(p_params), body;
|
||||
response["body"] = body;
|
||||
|
||||
Dictionary args = p_params["arguments"];
|
||||
int variable_id = args["variablesReference"];
|
||||
|
||||
HashMap<int, Array>::Iterator E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id);
|
||||
if (HashMap<int, Array>::Iterator E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); E) {
|
||||
Dictionary response = prepare_success_response(p_params);
|
||||
Dictionary body;
|
||||
response["body"] = body;
|
||||
|
||||
if (E) {
|
||||
if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsVariableType) {
|
||||
for (int i = 0; i < E->value.size(); i++) {
|
||||
Dictionary variable = E->value[i];
|
||||
variable.erase("type");
|
||||
}
|
||||
}
|
||||
|
||||
body["variables"] = E ? E->value : Array();
|
||||
return response;
|
||||
} else {
|
||||
return Dictionary();
|
||||
// If the requested variable is an object, it needs to be requested from the debuggee.
|
||||
ObjectID object_id = DebugAdapterProtocol::get_singleton()->search_object_id(variable_id);
|
||||
|
||||
if (object_id.is_null()) {
|
||||
return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN);
|
||||
}
|
||||
|
||||
DebugAdapterProtocol::get_singleton()->request_remote_object(object_id);
|
||||
}
|
||||
return Dictionary();
|
||||
}
|
||||
|
||||
Dictionary DebugAdapterParser::req_next(const Dictionary &p_params) const {
|
||||
|
|
@ -479,16 +488,27 @@ Dictionary DebugAdapterParser::req_stepIn(const Dictionary &p_params) const {
|
|||
}
|
||||
|
||||
Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const {
|
||||
Dictionary response = prepare_success_response(p_params), body;
|
||||
response["body"] = body;
|
||||
|
||||
Dictionary args = p_params["arguments"];
|
||||
String expression = args["expression"];
|
||||
int frame_id = args.has("frameId") ? static_cast<int>(args["frameId"]) : DebugAdapterProtocol::get_singleton()->_current_frame;
|
||||
|
||||
String value = EditorDebuggerNode::get_singleton()->get_var_value(args["expression"]);
|
||||
body["result"] = value;
|
||||
body["variablesReference"] = 0;
|
||||
if (HashMap<String, DAP::Variable>::Iterator E = DebugAdapterProtocol::get_singleton()->eval_list.find(expression); E) {
|
||||
Dictionary response = prepare_success_response(p_params);
|
||||
Dictionary body;
|
||||
response["body"] = body;
|
||||
|
||||
return response;
|
||||
DAP::Variable var = E->value;
|
||||
|
||||
body["result"] = var.value;
|
||||
body["variablesReference"] = var.variablesReference;
|
||||
|
||||
// Since an evaluation can alter the state of the debuggee, they are volatile, and should only be used once
|
||||
DebugAdapterProtocol::get_singleton()->eval_list.erase(E->key);
|
||||
return response;
|
||||
} else {
|
||||
DebugAdapterProtocol::get_singleton()->request_remote_evaluate(expression, frame_id);
|
||||
}
|
||||
return Dictionary();
|
||||
}
|
||||
|
||||
Dictionary DebugAdapterParser::req_godot_put_msg(const Dictionary &p_params) const {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ private:
|
|||
|
||||
_FORCE_INLINE_ bool is_valid_path(const String &p_path) const {
|
||||
// If path contains \, it's a Windows path, so we need to convert it to /, and check as case-insensitive.
|
||||
if (p_path.contains("\\")) {
|
||||
if (p_path.contains_char('\\')) {
|
||||
String project_path = ProjectSettings::get_singleton()->get_resource_path();
|
||||
String path = p_path.replace("\\", "/");
|
||||
return path.containsn(project_path);
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "core/io/json.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "editor/doc_tools.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
|
@ -186,6 +186,8 @@ void DebugAdapterProtocol::reset_stack_info() {
|
|||
|
||||
stackframe_list.clear();
|
||||
variable_list.clear();
|
||||
object_list.clear();
|
||||
object_pending_set.clear();
|
||||
}
|
||||
|
||||
int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
|
||||
|
|
@ -671,12 +673,194 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
|
|||
variable_list.insert(id, arr);
|
||||
return id;
|
||||
}
|
||||
case Variant::OBJECT: {
|
||||
// Objects have to be requested from the debuggee. This has do be done
|
||||
// in a lazy way, as retrieving object properties takes time.
|
||||
EncodedObjectAsID *encoded_obj = Object::cast_to<EncodedObjectAsID>(p_var);
|
||||
|
||||
// Object may be null; in that case, return early.
|
||||
if (!encoded_obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Object may have been already requested.
|
||||
ObjectID object_id = encoded_obj->get_object_id();
|
||||
if (object_list.has(object_id)) {
|
||||
return object_list[object_id];
|
||||
}
|
||||
|
||||
// Queue requesting the object.
|
||||
int id = variable_id++;
|
||||
object_list.insert(object_id, id);
|
||||
return id;
|
||||
}
|
||||
default:
|
||||
// Simple atomic stuff, or too complex to be manipulated
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugAdapterProtocol::parse_object(SceneDebuggerObject &p_obj) {
|
||||
// If the object is not on the pending list, we weren't expecting it. Ignore it.
|
||||
ObjectID object_id = p_obj.id;
|
||||
if (!object_pending_set.erase(object_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate DAP::Variable's with the object's properties. These properties will be divided by categories.
|
||||
Array properties;
|
||||
Array script_members;
|
||||
Array script_constants;
|
||||
Array script_node;
|
||||
DAP::Variable node_type;
|
||||
Array node_properties;
|
||||
|
||||
for (SceneDebuggerObject::SceneDebuggerProperty &property : p_obj.properties) {
|
||||
PropertyInfo &info = property.first;
|
||||
|
||||
// Script members ("Members/" prefix)
|
||||
if (info.name.begins_with("Members/")) {
|
||||
info.name = info.name.trim_prefix("Members/");
|
||||
script_members.push_back(parse_object_variable(property));
|
||||
}
|
||||
|
||||
// Script constants ("Constants/" prefix)
|
||||
else if (info.name.begins_with("Constants/")) {
|
||||
info.name = info.name.trim_prefix("Constants/");
|
||||
script_constants.push_back(parse_object_variable(property));
|
||||
}
|
||||
|
||||
// Script node ("Node/" prefix)
|
||||
else if (info.name.begins_with("Node/")) {
|
||||
info.name = info.name.trim_prefix("Node/");
|
||||
script_node.push_back(parse_object_variable(property));
|
||||
}
|
||||
|
||||
// Regular categories (with type Variant::NIL)
|
||||
else if (info.type == Variant::NIL) {
|
||||
if (!node_properties.is_empty()) {
|
||||
node_type.value = itos(node_properties.size());
|
||||
variable_list.insert(node_type.variablesReference, node_properties.duplicate());
|
||||
properties.push_back(node_type.to_json());
|
||||
}
|
||||
|
||||
node_type.name = info.name;
|
||||
node_type.type = "Category";
|
||||
node_type.variablesReference = variable_id++;
|
||||
node_properties.clear();
|
||||
}
|
||||
|
||||
// Regular properties.
|
||||
else {
|
||||
node_properties.push_back(parse_object_variable(property));
|
||||
}
|
||||
}
|
||||
|
||||
// Add the last category.
|
||||
if (!node_properties.is_empty()) {
|
||||
node_type.value = itos(node_properties.size());
|
||||
variable_list.insert(node_type.variablesReference, node_properties.duplicate());
|
||||
properties.push_back(node_type.to_json());
|
||||
}
|
||||
|
||||
// Add the script categories, in reverse order to be at the front of the array:
|
||||
// ( [members; constants; node; category1; category2; ...] )
|
||||
if (!script_node.is_empty()) {
|
||||
DAP::Variable node;
|
||||
node.name = "Node";
|
||||
node.type = "Category";
|
||||
node.value = itos(script_node.size());
|
||||
node.variablesReference = variable_id++;
|
||||
variable_list.insert(node.variablesReference, script_node);
|
||||
properties.push_front(node.to_json());
|
||||
}
|
||||
|
||||
if (!script_constants.is_empty()) {
|
||||
DAP::Variable constants;
|
||||
constants.name = "Constants";
|
||||
constants.type = "Category";
|
||||
constants.value = itos(script_constants.size());
|
||||
constants.variablesReference = variable_id++;
|
||||
variable_list.insert(constants.variablesReference, script_constants);
|
||||
properties.push_front(constants.to_json());
|
||||
}
|
||||
|
||||
if (!script_members.is_empty()) {
|
||||
DAP::Variable members;
|
||||
members.name = "Members";
|
||||
members.type = "Category";
|
||||
members.value = itos(script_members.size());
|
||||
members.variablesReference = variable_id++;
|
||||
variable_list.insert(members.variablesReference, script_members);
|
||||
properties.push_front(members.to_json());
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(!object_list.has(object_id));
|
||||
variable_list.insert(object_list[object_id], properties);
|
||||
}
|
||||
|
||||
void DebugAdapterProtocol::parse_evaluation(DebuggerMarshalls::ScriptStackVariable &p_var) {
|
||||
// If the eval is not on the pending list, we weren't expecting it. Ignore it.
|
||||
String eval = p_var.name;
|
||||
if (!eval_pending_list.erase(eval)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DAP::Variable variable;
|
||||
variable.name = p_var.name;
|
||||
variable.value = p_var.value;
|
||||
variable.type = Variant::get_type_name(p_var.value.get_type());
|
||||
variable.variablesReference = parse_variant(p_var.value);
|
||||
|
||||
eval_list.insert(variable.name, variable);
|
||||
}
|
||||
|
||||
const Variant DebugAdapterProtocol::parse_object_variable(const SceneDebuggerObject::SceneDebuggerProperty &p_property) {
|
||||
const PropertyInfo &info = p_property.first;
|
||||
const Variant &value = p_property.second;
|
||||
|
||||
DAP::Variable var;
|
||||
var.name = info.name;
|
||||
var.type = Variant::get_type_name(info.type);
|
||||
var.value = value;
|
||||
var.variablesReference = parse_variant(value);
|
||||
|
||||
return var.to_json();
|
||||
}
|
||||
|
||||
ObjectID DebugAdapterProtocol::search_object_id(DAPVarID p_var_id) {
|
||||
for (const KeyValue<ObjectID, DAPVarID> &E : object_list) {
|
||||
if (E.value == p_var_id) {
|
||||
return E.key;
|
||||
}
|
||||
}
|
||||
return ObjectID();
|
||||
}
|
||||
|
||||
bool DebugAdapterProtocol::request_remote_object(const ObjectID &p_object_id) {
|
||||
// If the object is already on the pending list, we don't need to request it again.
|
||||
if (object_pending_set.has(p_object_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_object(p_object_id);
|
||||
object_pending_set.insert(p_object_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebugAdapterProtocol::request_remote_evaluate(const String &p_eval, int p_stack_frame) {
|
||||
// If the eval is already on the pending list, we don't need to request it again
|
||||
if (eval_pending_list.has(p_eval)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_evaluate(p_eval, p_stack_frame);
|
||||
eval_pending_list.insert(p_eval);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DebugAdapterProtocol::process_message(const String &p_text) {
|
||||
JSON json;
|
||||
ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!");
|
||||
|
|
@ -966,7 +1150,7 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) {
|
|||
|
||||
List<int> scope_ids = stackframe_list.find(frame)->value;
|
||||
ERR_FAIL_COND(scope_ids.size() != 3);
|
||||
ERR_FAIL_INDEX(stack_var.type, 3);
|
||||
ERR_FAIL_INDEX(stack_var.type, 4);
|
||||
int var_id = scope_ids.get(stack_var.type);
|
||||
|
||||
DAP::Variable variable;
|
||||
|
|
@ -986,6 +1170,20 @@ void DebugAdapterProtocol::on_debug_data(const String &p_msg, const Array &p_dat
|
|||
return;
|
||||
}
|
||||
|
||||
if (p_msg == "scene:inspect_object") {
|
||||
// An object was requested from the debuggee; parse it.
|
||||
SceneDebuggerObject remote_obj;
|
||||
remote_obj.deserialize(p_data);
|
||||
|
||||
parse_object(remote_obj);
|
||||
} else if (p_msg == "evaluation_return") {
|
||||
// An evaluation was requested from the debuggee; parse it.
|
||||
DebuggerMarshalls::ScriptStackVariable remote_evaluation;
|
||||
remote_evaluation.deserialize(p_data);
|
||||
|
||||
parse_evaluation(remote_evaluation);
|
||||
}
|
||||
|
||||
notify_custom_data(p_msg, p_data);
|
||||
}
|
||||
|
||||
|
|
@ -1049,7 +1247,7 @@ DebugAdapterProtocol::DebugAdapterProtocol() {
|
|||
debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled));
|
||||
|
||||
debugger_node->get_default_debugger()->connect("stopped", callable_mp(this, &DebugAdapterProtocol::on_debug_stopped));
|
||||
debugger_node->get_default_debugger()->connect("output", callable_mp(this, &DebugAdapterProtocol::on_debug_output));
|
||||
debugger_node->get_default_debugger()->connect(SceneStringName(output), callable_mp(this, &DebugAdapterProtocol::on_debug_output));
|
||||
debugger_node->get_default_debugger()->connect("breaked", callable_mp(this, &DebugAdapterProtocol::on_debug_breaked));
|
||||
debugger_node->get_default_debugger()->connect("stack_dump", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_dump));
|
||||
debugger_node->get_default_debugger()->connect("stack_frame_vars", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_vars));
|
||||
|
|
|
|||
|
|
@ -31,12 +31,13 @@
|
|||
#ifndef DEBUG_ADAPTER_PROTOCOL_H
|
||||
#define DEBUG_ADAPTER_PROTOCOL_H
|
||||
|
||||
#include "core/io/stream_peer.h"
|
||||
#include "core/debugger/debugger_marshalls.h"
|
||||
#include "core/io/stream_peer_tcp.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
|
||||
#include "debug_adapter_parser.h"
|
||||
#include "debug_adapter_types.h"
|
||||
#include "scene/debugger/scene_debugger.h"
|
||||
|
||||
#define DAP_MAX_BUFFER_SIZE 4194304 // 4MB
|
||||
#define DAP_MAX_CLIENTS 8
|
||||
|
|
@ -75,6 +76,8 @@ class DebugAdapterProtocol : public Object {
|
|||
|
||||
friend class DebugAdapterParser;
|
||||
|
||||
using DAPVarID = int;
|
||||
|
||||
private:
|
||||
static DebugAdapterProtocol *singleton;
|
||||
DebugAdapterParser *parser = nullptr;
|
||||
|
|
@ -99,6 +102,13 @@ private:
|
|||
void reset_stack_info();
|
||||
|
||||
int parse_variant(const Variant &p_var);
|
||||
void parse_object(SceneDebuggerObject &p_obj);
|
||||
const Variant parse_object_variable(const SceneDebuggerObject::SceneDebuggerProperty &p_property);
|
||||
void parse_evaluation(DebuggerMarshalls::ScriptStackVariable &p_var);
|
||||
|
||||
ObjectID search_object_id(DAPVarID p_var_id);
|
||||
bool request_remote_object(const ObjectID &p_object_id);
|
||||
bool request_remote_evaluate(const String &p_eval, int p_stack_frame);
|
||||
|
||||
bool _initialized = false;
|
||||
bool _processing_breakpoint = false;
|
||||
|
|
@ -106,7 +116,7 @@ private:
|
|||
bool _processing_stackdump = false;
|
||||
int _remaining_vars = 0;
|
||||
int _current_frame = 0;
|
||||
uint64_t _request_timeout = 1000;
|
||||
uint64_t _request_timeout = 5000;
|
||||
bool _sync_breakpoints = false;
|
||||
|
||||
String _current_request;
|
||||
|
|
@ -114,10 +124,16 @@ private:
|
|||
|
||||
int breakpoint_id = 0;
|
||||
int stackframe_id = 0;
|
||||
int variable_id = 0;
|
||||
DAPVarID variable_id = 0;
|
||||
List<DAP::Breakpoint> breakpoint_list;
|
||||
HashMap<DAP::StackFrame, List<int>, DAP::StackFrame> stackframe_list;
|
||||
HashMap<int, Array> variable_list;
|
||||
HashMap<DAPVarID, Array> variable_list;
|
||||
|
||||
HashMap<ObjectID, DAPVarID> object_list;
|
||||
HashSet<ObjectID> object_pending_set;
|
||||
|
||||
HashMap<String, DAP::Variable> eval_list;
|
||||
HashSet<String> eval_pending_list;
|
||||
|
||||
public:
|
||||
friend class DebugAdapterServer;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "debug_adapter_server.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
|
@ -38,6 +37,7 @@
|
|||
int DebugAdapterServer::port_override = -1;
|
||||
|
||||
DebugAdapterServer::DebugAdapterServer() {
|
||||
// TODO: Move to editor_settings.cpp
|
||||
_EDITOR_DEF("network/debug_adapter/remote_port", remote_port);
|
||||
_EDITOR_DEF("network/debug_adapter/request_timeout", protocol._request_timeout);
|
||||
_EDITOR_DEF("network/debug_adapter/sync_breakpoints", protocol._sync_breakpoints);
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
|
|||
Variant &var = property.second;
|
||||
|
||||
if (pinfo.type == Variant::OBJECT) {
|
||||
if (var.get_type() == Variant::STRING) {
|
||||
if (var.is_string()) {
|
||||
String path = var;
|
||||
if (path.contains("::")) {
|
||||
// built-in resource
|
||||
|
|
@ -167,7 +167,7 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
|
|||
if (debug_obj->get_script() != var) {
|
||||
debug_obj->set_script(Ref<RefCounted>());
|
||||
Ref<Script> scr(var);
|
||||
if (!scr.is_null()) {
|
||||
if (scr.is_valid()) {
|
||||
ScriptInstance *scr_instance = scr->placeholder_instance_create(debug_obj);
|
||||
if (scr_instance) {
|
||||
debug_obj->set_script_and_instance(var, scr_instance);
|
||||
|
|
@ -223,7 +223,7 @@ Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
|
||||
void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_offset) {
|
||||
DebuggerMarshalls::ScriptStackVariable var;
|
||||
var.deserialize(p_array);
|
||||
String n = var.name;
|
||||
|
|
@ -248,6 +248,9 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
|
|||
case 2:
|
||||
type = "Globals/";
|
||||
break;
|
||||
case 3:
|
||||
type = "Evaluated/";
|
||||
break;
|
||||
default:
|
||||
type = "Unknown/";
|
||||
}
|
||||
|
|
@ -258,7 +261,15 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array) {
|
|||
pinfo.hint = h;
|
||||
pinfo.hint_string = hs;
|
||||
|
||||
variables->prop_list.push_back(pinfo);
|
||||
if ((p_offset == -1) || variables->prop_list.is_empty()) {
|
||||
variables->prop_list.push_back(pinfo);
|
||||
} else {
|
||||
List<PropertyInfo>::Element *current = variables->prop_list.front();
|
||||
for (int i = 0; i < p_offset; i++) {
|
||||
current = current->next();
|
||||
}
|
||||
variables->prop_list.insert_before(current, pinfo);
|
||||
}
|
||||
variables->prop_values[type + n] = v;
|
||||
variables->update();
|
||||
edit(variables);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
List<PropertyInfo> prop_list;
|
||||
HashMap<StringName, Variant> prop_values;
|
||||
|
||||
ObjectID get_remote_object_id() { return remote_object_id; };
|
||||
ObjectID get_remote_object_id() { return remote_object_id; }
|
||||
String get_title();
|
||||
|
||||
Variant get_variant(const StringName &p_name);
|
||||
|
|
@ -90,7 +90,7 @@ public:
|
|||
|
||||
// Stack Dump variables
|
||||
String get_stack_variable(const String &p_var);
|
||||
void add_stack_variable(const Array &p_arr);
|
||||
void add_stack_variable(const Array &p_arr, int p_offset = -1);
|
||||
void clear_stack_variables();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ EditorDebuggerNode::EditorDebuggerNode() {
|
|||
remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");
|
||||
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
|
||||
|
||||
if (Engine::get_singleton()->is_recovery_mode_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorRunBar::get_singleton()->get_pause_button()->connect(SceneStringName(pressed), callable_mp(this, &EditorDebuggerNode::_paused));
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +109,7 @@ 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("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_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_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));
|
||||
|
|
@ -119,7 +124,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
|
|||
|
||||
tabs->add_child(node);
|
||||
|
||||
node->set_name("Session " + itos(tabs->get_tab_count()));
|
||||
node->set_name(vformat(TTR("Session %d"), tabs->get_tab_count()));
|
||||
if (tabs->get_tab_count() > 1) {
|
||||
node->clear_style();
|
||||
tabs->set_tabs_visible(true);
|
||||
|
|
@ -159,9 +164,9 @@ void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_d
|
|||
} else {
|
||||
// If the script is built-in, it can be opened only if the scene is loaded in memory.
|
||||
int i = file.find("::");
|
||||
int j = file.rfind("(", i);
|
||||
int j = file.rfind_char('(', i);
|
||||
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
|
||||
file = file.substr(j + 1, file.find(")", i) - j - 1);
|
||||
file = file.substr(j + 1, file.find_char(')', i) - j - 1);
|
||||
}
|
||||
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
|
||||
stack_script = ResourceLoader::load(file);
|
||||
|
|
@ -182,9 +187,9 @@ void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_
|
|||
} else {
|
||||
// If the script is built-in, it can be opened only if the scene is loaded in memory.
|
||||
int i = file.find("::");
|
||||
int j = file.rfind("(", i);
|
||||
int j = file.rfind_char('(', i);
|
||||
if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path.
|
||||
file = file.substr(j + 1, file.find(")", i) - j - 1);
|
||||
file = file.substr(j + 1, file.find_char(')', i) - j - 1);
|
||||
}
|
||||
Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0));
|
||||
stack_script = ResourceLoader::load(file);
|
||||
|
|
@ -213,8 +218,8 @@ void EditorDebuggerNode::_bind_methods() {
|
|||
}
|
||||
|
||||
void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {
|
||||
p_undo_redo->set_method_notify_callback(_method_changeds, this);
|
||||
p_undo_redo->set_property_notify_callback(_property_changeds, this);
|
||||
p_undo_redo->set_method_notify_callback(_methods_changed, this);
|
||||
p_undo_redo->set_property_notify_callback(_properties_changed, this);
|
||||
}
|
||||
|
||||
EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
|
||||
|
|
@ -262,6 +267,10 @@ void EditorDebuggerNode::set_keep_open(bool p_keep_open) {
|
|||
}
|
||||
|
||||
Error EditorDebuggerNode::start(const String &p_uri) {
|
||||
if (Engine::get_singleton()->is_recovery_mode_hint()) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!p_uri.contains("://"), ERR_INVALID_PARAMETER);
|
||||
if (keep_open && current_uri == p_uri && server.is_valid()) {
|
||||
return OK;
|
||||
|
|
@ -303,7 +312,7 @@ void EditorDebuggerNode::stop(bool p_force) {
|
|||
});
|
||||
_break_state_changed();
|
||||
breakpoints.clear();
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(false, EditorUndoRedoManager::REMOTE_HISTORY);
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::REMOTE_HISTORY, false);
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
|
|
@ -330,7 +339,7 @@ void EditorDebuggerNode::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
if (!server.is_valid()) {
|
||||
if (server.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -417,18 +426,18 @@ void EditorDebuggerNode::_update_errors() {
|
|||
if (error_count == 0 && warning_count == 0) {
|
||||
debugger_button->set_text(TTR("Debugger"));
|
||||
debugger_button->remove_theme_color_override(SceneStringName(font_color));
|
||||
debugger_button->set_icon(Ref<Texture2D>());
|
||||
debugger_button->set_button_icon(Ref<Texture2D>());
|
||||
} else {
|
||||
debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")");
|
||||
if (error_count >= 1 && warning_count >= 1) {
|
||||
debugger_button->set_icon(get_editor_theme_icon(SNAME("ErrorWarning")));
|
||||
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("ErrorWarning")));
|
||||
// Use error color to represent the highest level of severity reported.
|
||||
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
} else if (error_count >= 1) {
|
||||
debugger_button->set_icon(get_editor_theme_icon(SNAME("Error")));
|
||||
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Error")));
|
||||
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
} else {
|
||||
debugger_button->set_icon(get_editor_theme_icon(SNAME("Warning")));
|
||||
debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Warning")));
|
||||
debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
}
|
||||
}
|
||||
|
|
@ -637,6 +646,13 @@ void EditorDebuggerNode::request_remote_tree() {
|
|||
get_current_debugger()->request_remote_tree();
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_remote_tree_select_requested(ObjectID p_id, int p_debugger) {
|
||||
if (p_debugger != tabs->get_current_tab()) {
|
||||
return;
|
||||
}
|
||||
remote_scene_tree->select_node(p_id);
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
|
||||
if (p_debugger != tabs->get_current_tab()) {
|
||||
return;
|
||||
|
|
@ -720,7 +736,7 @@ void EditorDebuggerNode::_breakpoints_cleared_in_tree(int p_debugger) {
|
|||
}
|
||||
|
||||
// Remote inspector/edit.
|
||||
void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {
|
||||
void EditorDebuggerNode::_methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount) {
|
||||
if (!singleton) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -729,7 +745,7 @@ void EditorDebuggerNode::_method_changeds(void *p_ud, Object *p_base, const Stri
|
|||
});
|
||||
}
|
||||
|
||||
void EditorDebuggerNode::_property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
|
||||
void EditorDebuggerNode::_properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value) {
|
||||
if (!singleton) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,11 +51,8 @@ class EditorDebuggerNode : public MarginContainer {
|
|||
public:
|
||||
enum CameraOverride {
|
||||
OVERRIDE_NONE,
|
||||
OVERRIDE_2D,
|
||||
OVERRIDE_3D_1, // 3D Viewport 1
|
||||
OVERRIDE_3D_2, // 3D Viewport 2
|
||||
OVERRIDE_3D_3, // 3D Viewport 3
|
||||
OVERRIDE_3D_4 // 3D Viewport 4
|
||||
OVERRIDE_INGAME,
|
||||
OVERRIDE_EDITORS,
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -132,6 +129,7 @@ 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 _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);
|
||||
|
|
@ -193,8 +191,8 @@ public:
|
|||
|
||||
// Remote inspector/edit.
|
||||
void request_remote_tree();
|
||||
static void _method_changeds(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
|
||||
static void _property_changeds(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
|
||||
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);
|
||||
|
||||
// LiveDebug
|
||||
void set_live_debugging(bool p_enabled);
|
||||
|
|
|
|||
|
|
@ -30,9 +30,7 @@
|
|||
|
||||
#include "editor_debugger_server.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
|
|
@ -77,8 +75,8 @@ Error EditorDebuggerServerTCP::start(const String &p_uri) {
|
|||
|
||||
// Optionally override
|
||||
if (!p_uri.is_empty() && p_uri != "tcp://") {
|
||||
String scheme, path;
|
||||
Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
|
||||
String scheme, path, fragment;
|
||||
Error err = p_uri.parse_url(scheme, bind_host, bind_port, path, fragment);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@
|
|||
|
||||
#include "editor_debugger_tree.h"
|
||||
|
||||
#include "editor/debugger/editor_debugger_node.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
|
|
@ -124,6 +126,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
|
|||
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("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());
|
||||
item_menu->reset_size();
|
||||
item_menu->popup();
|
||||
|
|
@ -144,23 +147,50 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
|
|||
/// |-E
|
||||
///
|
||||
void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger) {
|
||||
set_hide_root(false);
|
||||
|
||||
updating_scene_tree = true;
|
||||
const String last_path = get_selected_path();
|
||||
const String filter = SceneTreeDock::get_singleton()->get_filter();
|
||||
bool filter_changed = filter != last_filter;
|
||||
TreeItem *select_item = nullptr;
|
||||
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;
|
||||
|
||||
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
|
||||
List<Pair<TreeItem *, int>> parents;
|
||||
List<ParentItem> parents;
|
||||
for (const SceneDebuggerTree::RemoteNode &node : p_tree->nodes) {
|
||||
TreeItem *parent = nullptr;
|
||||
Pair<TreeItem *, TreeItem *> move_from_to;
|
||||
if (parents.size()) { // Find last parent.
|
||||
Pair<TreeItem *, int> &p = parents.front()->get();
|
||||
parent = p.first;
|
||||
if (!(--p.second)) { // If no child left, remove it.
|
||||
ParentItem &p = parents.front()->get();
|
||||
parent = p.tree_item;
|
||||
if (!(--p.child_count)) { // If no child left, remove it.
|
||||
parents.pop_front();
|
||||
|
||||
if (hide_filtered_out_parents && !filter.is_subsequence_ofn(parent->get_text(0))) {
|
||||
if (parent == get_root()) {
|
||||
set_hide_root(true);
|
||||
} else {
|
||||
move_from_to.first = parent;
|
||||
// Find the closest ancestor that matches the filter.
|
||||
for (const ParentItem p2 : parents) {
|
||||
move_from_to.second = p2.tree_item;
|
||||
if (p2.matches_filter || move_from_to.second == get_root()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!move_from_to.second) {
|
||||
move_from_to.second = get_root();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add this node.
|
||||
TreeItem *item = create_item(parent);
|
||||
item->set_text(0, node.name);
|
||||
|
|
@ -175,29 +205,41 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
|
|||
}
|
||||
item->set_metadata(0, node.id);
|
||||
|
||||
// Set current item as collapsed if necessary (root is never collapsed).
|
||||
String current_path;
|
||||
if (parent) {
|
||||
current_path += (String)parent->get_meta("node_path");
|
||||
|
||||
// Set current item as collapsed if necessary (root is never collapsed).
|
||||
if (!unfold_cache.has(node.id)) {
|
||||
item->set_collapsed(true);
|
||||
}
|
||||
}
|
||||
item->set_meta("node_path", current_path + "/" + item->get_text(0));
|
||||
|
||||
// Select previously selected node.
|
||||
if (debugger_id == p_debugger) { // Can use remote id.
|
||||
if (node.id == inspected_object_id) {
|
||||
item->select(0);
|
||||
if (filter_changed) {
|
||||
if (selection_uncollapse_all) {
|
||||
selection_uncollapse_all = false;
|
||||
|
||||
// Temporarily set to `false`, to allow caching the unfolds.
|
||||
updating_scene_tree = false;
|
||||
item->uncollapse_tree();
|
||||
updating_scene_tree = true;
|
||||
}
|
||||
|
||||
select_item = item;
|
||||
if (should_scroll) {
|
||||
scroll_item = item;
|
||||
}
|
||||
}
|
||||
} else { // Must use path
|
||||
if (last_path == _get_path(item)) {
|
||||
updating_scene_tree = false; // Force emission of new selection.
|
||||
item->select(0);
|
||||
if (filter_changed) {
|
||||
scroll_item = item;
|
||||
}
|
||||
updating_scene_tree = true;
|
||||
} 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;
|
||||
if (should_scroll) {
|
||||
scroll_item = item;
|
||||
}
|
||||
updating_scene_tree = true;
|
||||
}
|
||||
|
||||
// Add buttons.
|
||||
|
|
@ -229,7 +271,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
|
|||
|
||||
// Add in front of the parents stack if children are expected.
|
||||
if (node.child_count) {
|
||||
parents.push_front(Pair<TreeItem *, int>(item, node.child_count));
|
||||
parents.push_front(ParentItem(item, node.child_count, filter.is_subsequence_ofn(item->get_text(0))));
|
||||
} else {
|
||||
// Apply filters.
|
||||
while (parent) {
|
||||
|
|
@ -237,34 +279,79 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
|
|||
if (filter.is_subsequence_ofn(item->get_text(0))) {
|
||||
break; // Filter matches, must survive.
|
||||
}
|
||||
|
||||
parent->remove_child(item);
|
||||
memdelete(item);
|
||||
if (scroll_item == item) {
|
||||
if (select_item == item || scroll_item == item) {
|
||||
select_item = nullptr;
|
||||
scroll_item = nullptr;
|
||||
}
|
||||
|
||||
if (had_siblings) {
|
||||
break; // Parent must survive.
|
||||
}
|
||||
|
||||
item = parent;
|
||||
parent = item->get_parent();
|
||||
// Check if parent expects more children.
|
||||
for (const Pair<TreeItem *, int> &pair : parents) {
|
||||
if (pair.first == item) {
|
||||
for (ParentItem &pair : parents) {
|
||||
if (pair.tree_item == item) {
|
||||
parent = nullptr;
|
||||
break; // Might have more children.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move all children to the ancestor that matches the filter, if picked.
|
||||
if (move_from_to.first) {
|
||||
TreeItem *from = move_from_to.first;
|
||||
TypedArray<TreeItem> children = from->get_children();
|
||||
if (!children.is_empty()) {
|
||||
for (Variant &c : children) {
|
||||
TreeItem *ti = Object::cast_to<TreeItem>(c);
|
||||
from->remove_child(ti);
|
||||
move_from_to.second->add_child(ti);
|
||||
}
|
||||
|
||||
from->get_parent()->remove_child(from);
|
||||
memdelete(from);
|
||||
if (select_item == from || scroll_item == from) {
|
||||
select_item = nullptr;
|
||||
scroll_item = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree.
|
||||
|
||||
if (select_item) {
|
||||
select_item->select(0);
|
||||
}
|
||||
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree
|
||||
if (scroll_item) {
|
||||
callable_mp((Tree *)this, &Tree::scroll_to_item).call_deferred(scroll_item, false);
|
||||
scroll_to_item(scroll_item, false);
|
||||
}
|
||||
|
||||
last_filter = filter;
|
||||
updating_scene_tree = false;
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::select_node(ObjectID p_id) {
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
|
||||
Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
|
||||
if (get_button_id_at_position(p_point) != -1) {
|
||||
return Variant();
|
||||
|
|
@ -276,11 +363,14 @@ Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) {
|
|||
}
|
||||
|
||||
String path = selected->get_text(0);
|
||||
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
TextureRect *tf = memnew(TextureRect);
|
||||
tf->set_texture(selected->get_icon(0));
|
||||
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
|
||||
tf->set_custom_minimum_size(Size2(icon_size, icon_size));
|
||||
tf->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED);
|
||||
tf->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||
hb->add_child(tf);
|
||||
Label *label = memnew(Label(path));
|
||||
hb->add_child(label);
|
||||
|
|
@ -306,22 +396,7 @@ String EditorDebuggerTree::get_selected_path() {
|
|||
if (!get_selected()) {
|
||||
return "";
|
||||
}
|
||||
return _get_path(get_selected());
|
||||
}
|
||||
|
||||
String EditorDebuggerTree::_get_path(TreeItem *p_item) {
|
||||
ERR_FAIL_NULL_V(p_item, "");
|
||||
|
||||
if (p_item->get_parent() == nullptr) {
|
||||
return "/root";
|
||||
}
|
||||
String text = p_item->get_text(0);
|
||||
TreeItem *cur = p_item->get_parent();
|
||||
while (cur) {
|
||||
text = cur->get_text(0) + "/" + text;
|
||||
cur = cur->get_parent();
|
||||
}
|
||||
return "/" + text;
|
||||
return get_selected()->get_meta("node_path");
|
||||
}
|
||||
|
||||
void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
|
||||
|
|
@ -350,7 +425,7 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
|
|||
text = ".";
|
||||
} else {
|
||||
text = text.replace("/root/", "");
|
||||
int slash = text.find("/");
|
||||
int slash = text.find_char('/');
|
||||
if (slash < 0) {
|
||||
text = ".";
|
||||
} else {
|
||||
|
|
@ -359,6 +434,21 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
|
|||
}
|
||||
DisplayServer::get_singleton()->clipboard_set(text);
|
||||
} break;
|
||||
case ITEM_MENU_EXPAND_COLLAPSE: {
|
||||
TreeItem *s_item = get_selected();
|
||||
|
||||
if (!s_item) {
|
||||
s_item = get_root();
|
||||
if (!s_item) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool collapsed = s_item->is_any_collapsed();
|
||||
s_item->set_collapsed_recursive(!collapsed);
|
||||
|
||||
ensure_cursor_is_visible();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,20 +40,34 @@ class EditorDebuggerTree : public Tree {
|
|||
GDCLASS(EditorDebuggerTree, Tree);
|
||||
|
||||
private:
|
||||
struct ParentItem {
|
||||
TreeItem *tree_item;
|
||||
int child_count;
|
||||
bool matches_filter;
|
||||
|
||||
ParentItem(TreeItem *p_tree_item = nullptr, int p_child_count = 0, bool p_matches_filter = false) {
|
||||
tree_item = p_tree_item;
|
||||
child_count = p_child_count;
|
||||
matches_filter = p_matches_filter;
|
||||
}
|
||||
};
|
||||
|
||||
enum ItemMenu {
|
||||
ITEM_MENU_SAVE_REMOTE_NODE,
|
||||
ITEM_MENU_COPY_NODE_PATH,
|
||||
ITEM_MENU_EXPAND_COLLAPSE,
|
||||
};
|
||||
|
||||
ObjectID inspected_object_id;
|
||||
int debugger_id = 0;
|
||||
bool updating_scene_tree = false;
|
||||
bool scrolling_to_item = false;
|
||||
bool selection_uncollapse_all = false;
|
||||
HashSet<ObjectID> unfold_cache;
|
||||
PopupMenu *item_menu = nullptr;
|
||||
EditorFileDialog *file_dialog = nullptr;
|
||||
String last_filter;
|
||||
|
||||
String _get_path(TreeItem *p_item);
|
||||
void _scene_tree_folded(Object *p_obj);
|
||||
void _scene_tree_selected();
|
||||
void _scene_tree_rmb_selected(const Vector2 &p_position, MouseButton p_button);
|
||||
|
|
@ -77,6 +91,7 @@ public:
|
|||
ObjectID get_selected_object();
|
||||
int get_current_debugger(); // Would love to have one tree for every debugger.
|
||||
void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger);
|
||||
void select_node(ObjectID p_id);
|
||||
EditorDebuggerTree();
|
||||
};
|
||||
|
||||
|
|
|
|||
145
engine/editor/debugger/editor_expression_evaluator.cpp
Normal file
145
engine/editor/debugger/editor_expression_evaluator.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/**************************************************************************/
|
||||
/* editor_expression_evaluator.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_expression_evaluator.h"
|
||||
|
||||
#include "editor/debugger/editor_debugger_inspector.h"
|
||||
#include "editor/debugger/script_editor_debugger.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
|
||||
void EditorExpressionEvaluator::on_start() {
|
||||
expression_input->set_editable(false);
|
||||
evaluate_btn->set_disabled(true);
|
||||
|
||||
if (clear_on_run_checkbox->is_pressed()) {
|
||||
inspector->clear_stack_variables();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::set_editor_debugger(ScriptEditorDebugger *p_editor_debugger) {
|
||||
editor_debugger = p_editor_debugger;
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::add_value(const Array &p_array) {
|
||||
inspector->add_stack_variable(p_array, 0);
|
||||
inspector->set_v_scroll(0);
|
||||
inspector->set_h_scroll(0);
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_evaluate() {
|
||||
const String &expression = expression_input->get_text();
|
||||
if (expression.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!editor_debugger->is_session_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor_debugger->request_remote_evaluate(expression, editor_debugger->get_stack_script_frame());
|
||||
|
||||
expression_input->clear();
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_clear() {
|
||||
inspector->clear_stack_variables();
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_remote_object_selected(ObjectID p_id) {
|
||||
editor_debugger->emit_signal(SNAME("remote_object_requested"), p_id);
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_on_expression_input_changed(const String &p_expression) {
|
||||
evaluate_btn->set_disabled(p_expression.is_empty());
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_on_debugger_breaked(bool p_breaked, bool p_can_debug) {
|
||||
expression_input->set_editable(p_breaked);
|
||||
evaluate_btn->set_disabled(!p_breaked);
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_on_debugger_clear_execution(Ref<Script> p_stack_script) {
|
||||
expression_input->set_editable(false);
|
||||
evaluate_btn->set_disabled(true);
|
||||
}
|
||||
|
||||
void EditorExpressionEvaluator::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
EditorDebuggerNode::get_singleton()->connect("breaked", callable_mp(this, &EditorExpressionEvaluator::_on_debugger_breaked));
|
||||
EditorDebuggerNode::get_singleton()->connect("clear_execution", callable_mp(this, &EditorExpressionEvaluator::_on_debugger_clear_execution));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
EditorExpressionEvaluator::EditorExpressionEvaluator() {
|
||||
set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
add_child(hb);
|
||||
|
||||
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_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));
|
||||
hb->add_child(expression_input);
|
||||
|
||||
clear_on_run_checkbox = memnew(CheckBox);
|
||||
clear_on_run_checkbox->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
clear_on_run_checkbox->set_text(TTR("Clear on Run"));
|
||||
clear_on_run_checkbox->set_pressed(true);
|
||||
hb->add_child(clear_on_run_checkbox);
|
||||
|
||||
evaluate_btn = memnew(Button);
|
||||
evaluate_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
evaluate_btn->set_text(TTR("Evaluate"));
|
||||
evaluate_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorExpressionEvaluator::_evaluate));
|
||||
hb->add_child(evaluate_btn);
|
||||
|
||||
clear_btn = memnew(Button);
|
||||
clear_btn->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
clear_btn->set_text(TTR("Clear"));
|
||||
clear_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorExpressionEvaluator::_clear));
|
||||
hb->add_child(clear_btn);
|
||||
|
||||
inspector = memnew(EditorDebuggerInspector);
|
||||
inspector->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
inspector->set_property_name_style(EditorPropertyNameProcessor::STYLE_RAW);
|
||||
inspector->set_read_only(true);
|
||||
inspector->connect("object_selected", callable_mp(this, &EditorExpressionEvaluator::_remote_object_selected));
|
||||
inspector->set_use_filter(true);
|
||||
add_child(inspector);
|
||||
|
||||
expression_input->set_editable(false);
|
||||
evaluate_btn->set_disabled(true);
|
||||
}
|
||||
77
engine/editor/debugger/editor_expression_evaluator.h
Normal file
77
engine/editor/debugger/editor_expression_evaluator.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/**************************************************************************/
|
||||
/* editor_expression_evaluator.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_EXPRESSION_EVALUATOR_H
|
||||
#define EDITOR_EXPRESSION_EVALUATOR_H
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
|
||||
class Button;
|
||||
class CheckBox;
|
||||
class EditorDebuggerInspector;
|
||||
class LineEdit;
|
||||
class RemoteDebuggerPeer;
|
||||
class ScriptEditorDebugger;
|
||||
|
||||
class EditorExpressionEvaluator : public VBoxContainer {
|
||||
GDCLASS(EditorExpressionEvaluator, VBoxContainer)
|
||||
|
||||
private:
|
||||
Ref<RemoteDebuggerPeer> peer;
|
||||
|
||||
LineEdit *expression_input = nullptr;
|
||||
CheckBox *clear_on_run_checkbox = nullptr;
|
||||
Button *evaluate_btn = nullptr;
|
||||
Button *clear_btn = nullptr;
|
||||
|
||||
EditorDebuggerInspector *inspector = nullptr;
|
||||
|
||||
void _evaluate();
|
||||
void _clear();
|
||||
|
||||
void _remote_object_selected(ObjectID p_id);
|
||||
void _on_expression_input_changed(const String &p_expression);
|
||||
void _on_debugger_breaked(bool p_breaked, bool p_can_debug);
|
||||
void _on_debugger_clear_execution(Ref<Script> p_stack_script);
|
||||
|
||||
protected:
|
||||
ScriptEditorDebugger *editor_debugger = nullptr;
|
||||
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void on_start();
|
||||
void set_editor_debugger(ScriptEditorDebugger *p_editor_debugger);
|
||||
void add_value(const Array &p_array);
|
||||
|
||||
EditorExpressionEvaluator();
|
||||
};
|
||||
|
||||
#endif // EDITOR_EXPRESSION_EVALUATOR_H
|
||||
|
|
@ -31,7 +31,6 @@
|
|||
#include "editor_file_server.h"
|
||||
|
||||
#include "../editor_settings.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
|
||||
|
|
@ -271,9 +270,6 @@ void EditorFileServer::stop() {
|
|||
|
||||
EditorFileServer::EditorFileServer() {
|
||||
server.instantiate();
|
||||
|
||||
EDITOR_DEF("filesystem/file_server/port", 6010);
|
||||
EDITOR_DEF("filesystem/file_server/password", "");
|
||||
}
|
||||
|
||||
EditorFileServer::~EditorFileServer() {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,7 @@
|
|||
#ifndef EDITOR_FILE_SERVER_H
|
||||
#define EDITOR_FILE_SERVER_H
|
||||
|
||||
#include "core/io/packet_peer.h"
|
||||
#include "core/io/tcp_server.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ void EditorPerformanceProfiler::Monitor::reset() {
|
|||
|
||||
String EditorPerformanceProfiler::_create_label(float p_value, Performance::MonitorType p_type) {
|
||||
switch (p_type) {
|
||||
case Performance::MONITOR_TYPE_QUANTITY: {
|
||||
return TS->format_number(itos(p_value));
|
||||
}
|
||||
case Performance::MONITOR_TYPE_MEMORY: {
|
||||
return String::humanize_size(p_value);
|
||||
}
|
||||
|
|
@ -393,17 +396,23 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() {
|
|||
set_split_offset(340 * EDSCALE);
|
||||
|
||||
monitor_tree = memnew(Tree);
|
||||
monitor_tree->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
|
||||
monitor_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
monitor_tree->set_columns(2);
|
||||
monitor_tree->set_column_title(0, TTR("Monitor"));
|
||||
monitor_tree->set_column_expand(0, true);
|
||||
monitor_tree->set_column_title(1, TTR("Value"));
|
||||
monitor_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);
|
||||
monitor_tree->set_column_expand(1, false);
|
||||
monitor_tree->set_column_titles_visible(true);
|
||||
monitor_tree->connect("item_edited", callable_mp(this, &EditorPerformanceProfiler::_monitor_select));
|
||||
monitor_tree->create_item();
|
||||
monitor_tree->set_hide_root(true);
|
||||
monitor_tree->set_theme_type_variation("TreeSecondary");
|
||||
add_child(monitor_tree);
|
||||
|
||||
monitor_draw = memnew(Control);
|
||||
monitor_draw->set_custom_minimum_size(Size2(300, 0) * EDSCALE);
|
||||
monitor_draw->set_clip_contents(true);
|
||||
monitor_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPerformanceProfiler::_monitor_draw));
|
||||
monitor_draw->connect(SceneStringName(gui_input), callable_mp(this, &EditorPerformanceProfiler::_marker_input));
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define EDITOR_PERFORMANCE_PROFILER_H
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
#include "main/performance.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
|
|
|||
|
|
@ -30,11 +30,13 @@
|
|||
|
||||
#include "editor_profiler.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/io/image.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_run_bar.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/flow_container.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
void EditorProfiler::_make_metric_ptrs(Metric &m) {
|
||||
|
|
@ -150,6 +152,11 @@ Color EditorProfiler::_get_color_from_signature(const StringName &p_signature) c
|
|||
return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);
|
||||
}
|
||||
|
||||
int EditorProfiler::_get_zoom_left_border() const {
|
||||
const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom);
|
||||
return CLAMP(zoom_center - max_profiles_shown / 2, 0, frame_metrics.size() - max_profiles_shown);
|
||||
}
|
||||
|
||||
void EditorProfiler::_item_edited() {
|
||||
if (updating_frame) {
|
||||
return;
|
||||
|
|
@ -177,8 +184,8 @@ void EditorProfiler::_item_edited() {
|
|||
}
|
||||
|
||||
void EditorProfiler::_update_plot() {
|
||||
const int w = graph->get_size().width;
|
||||
const int h = graph->get_size().height;
|
||||
const int w = MAX(1, graph->get_size().width); // Clamp to 1 to prevent from crashing when profiler is autostarted.
|
||||
const int h = MAX(1, graph->get_size().height);
|
||||
bool reset_texture = false;
|
||||
const int desired_len = w * h * 4;
|
||||
|
||||
|
|
@ -235,12 +242,17 @@ void EditorProfiler::_update_plot() {
|
|||
|
||||
HashMap<StringName, int> prev_plots;
|
||||
|
||||
for (int i = 0; i < total_metrics * w / frame_metrics.size() - 1; i++) {
|
||||
const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom);
|
||||
const int left_border = _get_zoom_left_border();
|
||||
const int profiles_drawn = CLAMP(total_metrics - left_border, 0, max_profiles_shown);
|
||||
const int pixel_cols = (profiles_drawn * w) / max_profiles_shown - 1;
|
||||
|
||||
for (int i = 0; i < pixel_cols; i++) {
|
||||
for (int j = 0; j < h * 4; j++) {
|
||||
column[j] = 0;
|
||||
}
|
||||
|
||||
int current = i * frame_metrics.size() / w;
|
||||
int current = (i * max_profiles_shown / w) + left_border;
|
||||
|
||||
for (const StringName &E : plot_sigs) {
|
||||
const Metric &m = _get_frame_metric(current);
|
||||
|
|
@ -388,10 +400,10 @@ void EditorProfiler::_update_frame() {
|
|||
|
||||
void EditorProfiler::_update_button_text() {
|
||||
if (activate->is_pressed()) {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
activate->set_text(TTR("Stop"));
|
||||
} else {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
activate->set_text(TTR("Start"));
|
||||
}
|
||||
}
|
||||
|
|
@ -416,14 +428,19 @@ void EditorProfiler::_internal_profiles_pressed() {
|
|||
_combo_changed(0);
|
||||
}
|
||||
|
||||
void EditorProfiler::_autostart_toggled(bool p_toggled_on) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_profiler", p_toggled_on);
|
||||
EditorRunBar::get_singleton()->update_profiler_autostart_indicator();
|
||||
}
|
||||
|
||||
void EditorProfiler::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear")));
|
||||
|
||||
theme_cache.seek_line_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
|
||||
theme_cache.seek_line_color.a = 0.8;
|
||||
|
|
@ -443,10 +460,12 @@ void EditorProfiler::_graph_tex_draw() {
|
|||
}
|
||||
if (seeking) {
|
||||
int frame = cursor_metric_edit->get_value() - _get_frame_metric(0).frame_number;
|
||||
int cur_x = (2 * frame + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
|
||||
frame = frame - _get_zoom_left_border() + 1;
|
||||
int cur_x = (frame * graph->get_size().width * Math::exp(graph_zoom)) / frame_metrics.size();
|
||||
cur_x = CLAMP(cur_x, 0, graph->get_size().width);
|
||||
graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_color);
|
||||
}
|
||||
if (hover_metric > -1 && hover_metric < total_metrics) {
|
||||
if (hover_metric > -1) {
|
||||
int cur_x = (2 * hover_metric + 1) * graph->get_size().x / (2 * frame_metrics.size()) + 1;
|
||||
graph->draw_line(Vector2(cur_x, 0), Vector2(cur_x, graph->get_size().y), theme_cache.seek_line_hover_color);
|
||||
}
|
||||
|
|
@ -474,22 +493,17 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
|
|||
Ref<InputEventMouse> me = p_ev;
|
||||
Ref<InputEventMouseButton> mb = p_ev;
|
||||
Ref<InputEventMouseMotion> mm = p_ev;
|
||||
MouseButton button_idx = mb.is_valid() ? mb->get_button_index() : MouseButton();
|
||||
|
||||
if (
|
||||
(mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) ||
|
||||
(mb.is_valid() && button_idx == MouseButton::LEFT && mb->is_pressed()) ||
|
||||
(mm.is_valid())) {
|
||||
int x = me->get_position().x - 1;
|
||||
hover_metric = x * frame_metrics.size() / graph->get_size().width;
|
||||
|
||||
x = x * frame_metrics.size() / graph->get_size().width;
|
||||
|
||||
hover_metric = x;
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if (x >= frame_metrics.size()) {
|
||||
x = frame_metrics.size() - 1;
|
||||
}
|
||||
x = x / Math::exp(graph_zoom) + _get_zoom_left_border();
|
||||
x = CLAMP(x, 0, frame_metrics.size() - 1);
|
||||
|
||||
if (mb.is_valid() || (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
|
||||
updating_frame = true;
|
||||
|
|
@ -512,9 +526,34 @@ void EditorProfiler::_graph_tex_input(const Ref<InputEvent> &p_ev) {
|
|||
frame_delay->start();
|
||||
}
|
||||
}
|
||||
|
||||
graph->queue_redraw();
|
||||
}
|
||||
|
||||
if (graph_zoom > 0 && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::MIDDLE) || mm->get_button_mask().has_flag(MouseButtonMask::RIGHT))) {
|
||||
// Panning.
|
||||
const int max_profiles_shown = frame_metrics.size() / Math::exp(graph_zoom);
|
||||
pan_accumulator += (float)mm->get_relative().x * max_profiles_shown / graph->get_size().width;
|
||||
|
||||
if (Math::abs(pan_accumulator) > 1) {
|
||||
zoom_center = CLAMP(zoom_center - (int)pan_accumulator, max_profiles_shown / 2, frame_metrics.size() - max_profiles_shown / 2);
|
||||
pan_accumulator -= (int)pan_accumulator;
|
||||
_update_plot();
|
||||
}
|
||||
}
|
||||
|
||||
if (button_idx == MouseButton::WHEEL_DOWN) {
|
||||
// Zooming.
|
||||
graph_zoom = MAX(-0.05 + graph_zoom, 0);
|
||||
_update_plot();
|
||||
} else if (button_idx == MouseButton::WHEEL_UP) {
|
||||
if (graph_zoom == 0) {
|
||||
zoom_center = me->get_position().x;
|
||||
zoom_center = zoom_center * frame_metrics.size() / graph->get_size().width;
|
||||
}
|
||||
graph_zoom = MIN(0.05 + graph_zoom, 2);
|
||||
_update_plot();
|
||||
}
|
||||
|
||||
graph->queue_redraw();
|
||||
}
|
||||
|
||||
void EditorProfiler::disable_seeking() {
|
||||
|
|
@ -539,9 +578,10 @@ void EditorProfiler::set_enabled(bool p_enable, bool p_clear) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorProfiler::set_pressed(bool p_pressed) {
|
||||
void EditorProfiler::set_profiling(bool p_pressed) {
|
||||
activate->set_pressed(p_pressed);
|
||||
_update_button_text();
|
||||
emit_signal(SNAME("enable_profiling"), activate->is_pressed());
|
||||
}
|
||||
|
||||
bool EditorProfiler::is_profiling() {
|
||||
|
|
@ -619,21 +659,39 @@ Vector<Vector<String>> EditorProfiler::get_data_as_csv() const {
|
|||
|
||||
EditorProfiler::EditorProfiler() {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);
|
||||
add_child(hb);
|
||||
|
||||
FlowContainer *container = memnew(FlowContainer);
|
||||
container->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);
|
||||
container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);
|
||||
hb->add_child(container);
|
||||
|
||||
activate = memnew(Button);
|
||||
activate->set_toggle_mode(true);
|
||||
activate->set_disabled(true);
|
||||
activate->set_text(TTR("Start"));
|
||||
activate->connect(SceneStringName(pressed), callable_mp(this, &EditorProfiler::_activate_pressed));
|
||||
hb->add_child(activate);
|
||||
container->add_child(activate);
|
||||
|
||||
clear_button = memnew(Button);
|
||||
clear_button->set_text(TTR("Clear"));
|
||||
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorProfiler::_clear_pressed));
|
||||
clear_button->set_disabled(true);
|
||||
hb->add_child(clear_button);
|
||||
container->add_child(clear_button);
|
||||
|
||||
hb->add_child(memnew(Label(TTR("Measure:"))));
|
||||
CheckBox *autostart_checkbox = memnew(CheckBox);
|
||||
autostart_checkbox->set_text(TTR("Autostart"));
|
||||
autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false));
|
||||
autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorProfiler::_autostart_toggled));
|
||||
container->add_child(autostart_checkbox);
|
||||
|
||||
HBoxContainer *hb_measure = memnew(HBoxContainer);
|
||||
hb_measure->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
|
||||
container->add_child(hb_measure);
|
||||
|
||||
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
|
||||
|
||||
display_mode = memnew(OptionButton);
|
||||
display_mode->add_item(TTR("Frame Time (ms)"));
|
||||
|
|
@ -642,9 +700,13 @@ EditorProfiler::EditorProfiler() {
|
|||
display_mode->add_item(TTR("Physics Frame %"));
|
||||
display_mode->connect(SceneStringName(item_selected), callable_mp(this, &EditorProfiler::_combo_changed));
|
||||
|
||||
hb->add_child(display_mode);
|
||||
hb_measure->add_child(display_mode);
|
||||
|
||||
hb->add_child(memnew(Label(TTR("Time:"))));
|
||||
HBoxContainer *hb_time = memnew(HBoxContainer);
|
||||
hb_time->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
|
||||
container->add_child(hb_time);
|
||||
|
||||
hb_time->add_child(memnew(Label(TTR("Time:"))));
|
||||
|
||||
display_time = memnew(OptionButton);
|
||||
// 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.
|
||||
|
|
@ -653,28 +715,28 @@ EditorProfiler::EditorProfiler() {
|
|||
display_time->add_item(TTR("Self"));
|
||||
display_time->set_tooltip_text(TTR("Inclusive: Includes time from other functions called by this function.\nUse this to spot bottlenecks.\n\nSelf: Only count the time spent in the function itself, not in other functions called by that function.\nUse this to find individual functions to optimize."));
|
||||
display_time->connect(SceneStringName(item_selected), callable_mp(this, &EditorProfiler::_combo_changed));
|
||||
|
||||
hb->add_child(display_time);
|
||||
hb_time->add_child(display_time);
|
||||
|
||||
display_internal_profiles = memnew(CheckButton(TTR("Display internal functions")));
|
||||
display_internal_profiles->set_visible(EDITOR_GET("debugger/profile_native_calls"));
|
||||
display_internal_profiles->set_pressed(false);
|
||||
display_internal_profiles->connect(SceneStringName(pressed), callable_mp(this, &EditorProfiler::_internal_profiles_pressed));
|
||||
hb->add_child(display_internal_profiles);
|
||||
container->add_child(display_internal_profiles);
|
||||
|
||||
hb->add_spacer();
|
||||
HBoxContainer *hb_frame = memnew(HBoxContainer);
|
||||
hb_frame->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
|
||||
hb_frame->set_v_size_flags(SIZE_SHRINK_BEGIN);
|
||||
hb->add_child(hb_frame);
|
||||
|
||||
hb->add_child(memnew(Label(TTR("Frame #:"))));
|
||||
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
|
||||
|
||||
cursor_metric_edit = memnew(SpinBox);
|
||||
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
|
||||
cursor_metric_edit->set_value(0);
|
||||
cursor_metric_edit->set_editable(false);
|
||||
hb->add_child(cursor_metric_edit);
|
||||
hb_frame->add_child(cursor_metric_edit);
|
||||
cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorProfiler::_cursor_metric_changed));
|
||||
|
||||
hb->add_theme_constant_override("separation", 8 * EDSCALE);
|
||||
|
||||
h_split = memnew(HSplitContainer);
|
||||
add_child(h_split);
|
||||
h_split->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
|
@ -699,9 +761,11 @@ EditorProfiler::EditorProfiler() {
|
|||
variables->set_column_expand(2, false);
|
||||
variables->set_column_clip_content(2, true);
|
||||
variables->set_column_custom_minimum_width(2, 50 * EDSCALE);
|
||||
variables->set_theme_type_variation("TreeSecondary");
|
||||
variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited));
|
||||
|
||||
graph = memnew(TextureRect);
|
||||
graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0));
|
||||
graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||
graph->set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
graph->connect(SceneStringName(draw), callable_mp(this, &EditorProfiler::_graph_tex_draw));
|
||||
|
|
|
|||
|
|
@ -104,6 +104,11 @@ private:
|
|||
TextureRect *graph = nullptr;
|
||||
Ref<ImageTexture> graph_texture;
|
||||
Vector<uint8_t> graph_image;
|
||||
|
||||
float graph_zoom = 0.0f;
|
||||
float pan_accumulator = 0.0f;
|
||||
int zoom_center = -1;
|
||||
|
||||
Tree *variables = nullptr;
|
||||
HSplitContainer *h_split = nullptr;
|
||||
|
||||
|
|
@ -138,6 +143,7 @@ private:
|
|||
|
||||
void _activate_pressed();
|
||||
void _clear_pressed();
|
||||
void _autostart_toggled(bool p_toggled_on);
|
||||
|
||||
void _internal_profiles_pressed();
|
||||
|
||||
|
|
@ -154,6 +160,7 @@ private:
|
|||
void _graph_tex_input(const Ref<InputEvent> &p_ev);
|
||||
|
||||
Color _get_color_from_signature(const StringName &p_signature) const;
|
||||
int _get_zoom_left_border() const;
|
||||
|
||||
void _cursor_metric_changed(double);
|
||||
|
||||
|
|
@ -168,7 +175,7 @@ protected:
|
|||
public:
|
||||
void add_frame_metric(const Metric &p_metric, bool p_final = false);
|
||||
void set_enabled(bool p_enable, bool p_clear = true);
|
||||
void set_pressed(bool p_pressed);
|
||||
void set_profiling(bool p_pressed);
|
||||
bool is_profiling();
|
||||
bool is_seeking() { return seeking; }
|
||||
void disable_seeking();
|
||||
|
|
|
|||
|
|
@ -30,12 +30,20 @@
|
|||
|
||||
#include "editor_visual_profiler.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/io/image.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_run_bar.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/flow_container.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
|
||||
void EditorVisualProfiler::set_hardware_info(const String &p_cpu_name, const String &p_gpu_name) {
|
||||
cpu_name = p_cpu_name;
|
||||
gpu_name = p_gpu_name;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void EditorVisualProfiler::add_frame_metric(const Metric &p_metric) {
|
||||
++last_metric;
|
||||
if (last_metric >= frame_metrics.size()) {
|
||||
|
|
@ -103,6 +111,8 @@ void EditorVisualProfiler::clear() {
|
|||
variables->clear();
|
||||
//activate->set_pressed(false);
|
||||
|
||||
graph_limit = 1000.0f / CLAMP(int(EDITOR_GET("debugger/profiler_target_fps")), 1, 1000);
|
||||
|
||||
updating_frame = true;
|
||||
cursor_metric_edit->set_min(0);
|
||||
cursor_metric_edit->set_max(0);
|
||||
|
|
@ -146,8 +156,8 @@ void EditorVisualProfiler::_item_selected() {
|
|||
}
|
||||
|
||||
void EditorVisualProfiler::_update_plot() {
|
||||
const int w = graph->get_size().width;
|
||||
const int h = graph->get_size().height;
|
||||
const int w = graph->get_size().width + 1; // `+1` is to prevent from crashing when visual profiler is auto started.
|
||||
const int h = graph->get_size().height + 1;
|
||||
|
||||
bool reset_texture = false;
|
||||
|
||||
|
|
@ -408,12 +418,12 @@ void EditorVisualProfiler::_update_frame(bool p_focus_selected) {
|
|||
|
||||
void EditorVisualProfiler::_activate_pressed() {
|
||||
if (activate->is_pressed()) {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
activate->set_text(TTR("Stop"));
|
||||
_clear_pressed(); //always clear on start
|
||||
clear_button->set_disabled(false);
|
||||
} else {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
activate->set_text(TTR("Start"));
|
||||
}
|
||||
emit_signal(SNAME("enable_profiling"), activate->is_pressed());
|
||||
|
|
@ -425,18 +435,19 @@ void EditorVisualProfiler::_clear_pressed() {
|
|||
_update_plot();
|
||||
}
|
||||
|
||||
void EditorVisualProfiler::_autostart_toggled(bool p_toggled_on) {
|
||||
EditorSettings::get_singleton()->set_project_metadata("debug_options", "autostart_visual_profiler", p_toggled_on);
|
||||
EditorRunBar::get_singleton()->update_profiler_autostart_indicator();
|
||||
}
|
||||
|
||||
void EditorVisualProfiler::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
if (is_layout_rtl()) {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("PlayBackwards")));
|
||||
} else {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
}
|
||||
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -486,8 +497,8 @@ void EditorVisualProfiler::_graph_tex_draw() {
|
|||
graph->draw_string(font, Vector2(half_width * 2 - font->get_string_size(limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x - 2, frame_y - 2), limit_str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));
|
||||
}
|
||||
|
||||
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1));
|
||||
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU:", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1));
|
||||
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x, font->get_ascent(font_size) + 2), "CPU: " + cpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));
|
||||
graph->draw_string(font, Vector2(font->get_string_size("X", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x + graph->get_size().width / 2, font->get_ascent(font_size) + 2), "GPU: " + gpu_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color * Color(1, 1, 1, 0.75));
|
||||
}
|
||||
|
||||
void EditorVisualProfiler::_graph_tex_mouse_exit() {
|
||||
|
|
@ -654,10 +665,10 @@ void EditorVisualProfiler::_bind_methods() {
|
|||
|
||||
void EditorVisualProfiler::_update_button_text() {
|
||||
if (activate->is_pressed()) {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
activate->set_text(TTR("Stop"));
|
||||
} else {
|
||||
activate->set_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
activate->set_button_icon(get_editor_theme_icon(SNAME("Play")));
|
||||
activate->set_text(TTR("Start"));
|
||||
}
|
||||
}
|
||||
|
|
@ -666,9 +677,10 @@ void EditorVisualProfiler::set_enabled(bool p_enable) {
|
|||
activate->set_disabled(!p_enable);
|
||||
}
|
||||
|
||||
void EditorVisualProfiler::set_pressed(bool p_pressed) {
|
||||
activate->set_pressed(p_pressed);
|
||||
void EditorVisualProfiler::set_profiling(bool p_profiling) {
|
||||
activate->set_pressed(p_profiling);
|
||||
_update_button_text();
|
||||
emit_signal(SNAME("enable_profiling"), activate->is_pressed());
|
||||
}
|
||||
|
||||
bool EditorVisualProfiler::is_profiling() {
|
||||
|
|
@ -731,49 +743,68 @@ Vector<Vector<String>> EditorVisualProfiler::get_data_as_csv() const {
|
|||
|
||||
EditorVisualProfiler::EditorVisualProfiler() {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);
|
||||
add_child(hb);
|
||||
|
||||
FlowContainer *container = memnew(FlowContainer);
|
||||
container->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);
|
||||
container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);
|
||||
hb->add_child(container);
|
||||
|
||||
activate = memnew(Button);
|
||||
activate->set_toggle_mode(true);
|
||||
activate->set_disabled(true);
|
||||
activate->set_text(TTR("Start"));
|
||||
activate->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_activate_pressed));
|
||||
hb->add_child(activate);
|
||||
container->add_child(activate);
|
||||
|
||||
clear_button = memnew(Button);
|
||||
clear_button->set_text(TTR("Clear"));
|
||||
clear_button->set_disabled(true);
|
||||
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_clear_pressed));
|
||||
hb->add_child(clear_button);
|
||||
container->add_child(clear_button);
|
||||
|
||||
hb->add_child(memnew(Label(TTR("Measure:"))));
|
||||
CheckBox *autostart_checkbox = memnew(CheckBox);
|
||||
autostart_checkbox->set_text(TTR("Autostart"));
|
||||
autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false));
|
||||
autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorVisualProfiler::_autostart_toggled));
|
||||
container->add_child(autostart_checkbox);
|
||||
|
||||
HBoxContainer *hb_measure = memnew(HBoxContainer);
|
||||
hb_measure->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
|
||||
container->add_child(hb_measure);
|
||||
|
||||
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
|
||||
|
||||
display_mode = memnew(OptionButton);
|
||||
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));
|
||||
|
||||
hb->add_child(display_mode);
|
||||
hb_measure->add_child(display_mode);
|
||||
|
||||
frame_relative = memnew(CheckBox(TTR("Fit to Frame")));
|
||||
frame_relative->set_pressed(true);
|
||||
hb->add_child(frame_relative);
|
||||
container->add_child(frame_relative);
|
||||
frame_relative->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_update_plot));
|
||||
linked = memnew(CheckBox(TTR("Linked")));
|
||||
linked->set_pressed(true);
|
||||
hb->add_child(linked);
|
||||
container->add_child(linked);
|
||||
linked->connect(SceneStringName(pressed), callable_mp(this, &EditorVisualProfiler::_update_plot));
|
||||
|
||||
hb->add_spacer();
|
||||
HBoxContainer *hb_frame = memnew(HBoxContainer);
|
||||
hb_frame->add_theme_constant_override(SNAME("separation"), 2 * EDSCALE);
|
||||
hb_frame->set_v_size_flags(SIZE_SHRINK_BEGIN);
|
||||
hb->add_child(hb_frame);
|
||||
|
||||
hb->add_child(memnew(Label(TTR("Frame #:"))));
|
||||
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
|
||||
|
||||
cursor_metric_edit = memnew(SpinBox);
|
||||
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
|
||||
hb->add_child(cursor_metric_edit);
|
||||
hb_frame->add_child(cursor_metric_edit);
|
||||
cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorVisualProfiler::_cursor_metric_changed));
|
||||
|
||||
hb->add_theme_constant_override("separation", 8 * EDSCALE);
|
||||
|
||||
h_split = memnew(HSplitContainer);
|
||||
add_child(h_split);
|
||||
h_split->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
|
@ -797,9 +828,11 @@ EditorVisualProfiler::EditorVisualProfiler() {
|
|||
variables->set_column_expand(2, false);
|
||||
variables->set_column_clip_content(2, true);
|
||||
variables->set_column_custom_minimum_width(2, 75 * EDSCALE);
|
||||
variables->set_theme_type_variation("TreeSecondary");
|
||||
variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected));
|
||||
|
||||
graph = memnew(TextureRect);
|
||||
graph->set_custom_minimum_size(Size2(250 * EDSCALE, 0));
|
||||
graph->set_expand_mode(TextureRect::EXPAND_IGNORE_SIZE);
|
||||
graph->set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
graph->connect(SceneStringName(draw), callable_mp(this, &EditorVisualProfiler::_graph_tex_draw));
|
||||
|
|
@ -812,6 +845,8 @@ EditorVisualProfiler::EditorVisualProfiler() {
|
|||
int metric_size = CLAMP(int(EDITOR_GET("debugger/profiler_frame_history_size")), 60, 10000);
|
||||
frame_metrics.resize(metric_size);
|
||||
|
||||
graph_limit = 1000.0f / CLAMP(int(EDITOR_GET("debugger/profiler_target_fps")), 1, 1000);
|
||||
|
||||
frame_delay = memnew(Timer);
|
||||
frame_delay->set_wait_time(0.1);
|
||||
frame_delay->set_one_shot(true);
|
||||
|
|
|
|||
|
|
@ -98,6 +98,9 @@ private:
|
|||
|
||||
float graph_limit = 1000.0f / 60;
|
||||
|
||||
String cpu_name;
|
||||
String gpu_name;
|
||||
|
||||
bool seeking = false;
|
||||
|
||||
Timer *frame_delay = nullptr;
|
||||
|
|
@ -109,6 +112,7 @@ private:
|
|||
|
||||
void _activate_pressed();
|
||||
void _clear_pressed();
|
||||
void _autostart_toggled(bool p_toggled_on);
|
||||
|
||||
String _get_time_as_text(float p_time);
|
||||
|
||||
|
|
@ -135,9 +139,10 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_hardware_info(const String &p_cpu_name, const String &p_gpu_name);
|
||||
void add_frame_metric(const Metric &p_metric);
|
||||
void set_enabled(bool p_enable);
|
||||
void set_pressed(bool p_pressed);
|
||||
void set_profiling(bool p_profiling);
|
||||
bool is_profiling();
|
||||
bool is_seeking() { return seeking; }
|
||||
void disable_seeking();
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "core/string/ustring.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/debugger/debug_adapter/debug_adapter_protocol.h"
|
||||
#include "editor/debugger/editor_expression_evaluator.h"
|
||||
#include "editor/debugger/editor_performance_profiler.h"
|
||||
#include "editor/debugger/editor_profiler.h"
|
||||
#include "editor/debugger/editor_visual_profiler.h"
|
||||
|
|
@ -60,13 +61,10 @@
|
|||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/texture_button.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "servers/debugger/servers_debugger.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
|
|
@ -94,9 +92,9 @@ void ScriptEditorDebugger::debug_copy() {
|
|||
void ScriptEditorDebugger::debug_skip_breakpoints() {
|
||||
skip_breakpoints_value = !skip_breakpoints_value;
|
||||
if (skip_breakpoints_value) {
|
||||
skip_breakpoints->set_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));
|
||||
skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn")));
|
||||
} else {
|
||||
skip_breakpoints->set_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));
|
||||
skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff")));
|
||||
}
|
||||
|
||||
Array msg;
|
||||
|
|
@ -252,6 +250,13 @@ const SceneDebuggerTree *ScriptEditorDebugger::get_remote_tree() {
|
|||
return scene_tree;
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::request_remote_evaluate(const String &p_expression, int p_stack_frame) {
|
||||
Array msg;
|
||||
msg.push_back(p_expression);
|
||||
msg.push_back(p_stack_frame);
|
||||
_put_msg("evaluate", msg);
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value) {
|
||||
Array msg;
|
||||
msg.push_back(p_obj_id);
|
||||
|
|
@ -307,7 +312,7 @@ void ScriptEditorDebugger::_thread_debug_enter(uint64_t p_thread_id) {
|
|||
ThreadDebugged &td = threads_debugged[p_thread_id];
|
||||
_set_reason_text(td.error, MESSAGE_ERROR);
|
||||
emit_signal(SNAME("breaked"), true, td.can_debug, td.error, td.has_stackdump);
|
||||
if (!td.error.is_empty()) {
|
||||
if (!td.error.is_empty() && EDITOR_GET("debugger/auto_switch_to_stack_trace")) {
|
||||
tabs->set_current_tab(0);
|
||||
}
|
||||
inspector->clear_cache(); // Take a chance to force remote objects update.
|
||||
|
|
@ -496,7 +501,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
|
|||
} break;
|
||||
}
|
||||
EditorNode::get_log()->add_message(output_strings[i], msg_type);
|
||||
emit_signal(SNAME("output"), output_strings[i], msg_type);
|
||||
emit_signal(SceneStringName(output), output_strings[i], msg_type);
|
||||
}
|
||||
} else if (p_msg == "performance:profile_frame") {
|
||||
Vector<float> frame_data;
|
||||
|
|
@ -505,6 +510,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
|
|||
frame_data.write[i] = p_data[i];
|
||||
}
|
||||
performance_profiler->add_profile_frame(frame_data);
|
||||
} else if (p_msg == "visual:hardware_info") {
|
||||
const String cpu_name = p_data[0];
|
||||
const String gpu_name = p_data[1];
|
||||
visual_profiler->set_hardware_info(cpu_name, gpu_name);
|
||||
} else if (p_msg == "visual:profile_frame") {
|
||||
ServersDebugger::VisualProfilerFrame frame;
|
||||
frame.deserialize(p_data);
|
||||
|
|
@ -534,7 +543,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
|
|||
time_vals.push_back(oe.sec);
|
||||
time_vals.push_back(oe.msec);
|
||||
bool e;
|
||||
String time = String("%d:%02d:%02d:%04d").sprintf(time_vals, &e);
|
||||
String time = String("%d:%02d:%02d:%03d").sprintf(time_vals, &e);
|
||||
|
||||
// Rest of the error data.
|
||||
bool source_is_project_file = oe.source_file.begins_with("res://");
|
||||
|
|
@ -798,6 +807,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
|
|||
} else if (p_msg == "request_quit") {
|
||||
emit_signal(SNAME("stop_requested"));
|
||||
_stop_and_notify();
|
||||
} else if (p_msg == "remote_node_clicked") {
|
||||
if (!p_data.is_empty()) {
|
||||
emit_signal(SNAME("remote_tree_select_requested"), p_data[0]);
|
||||
}
|
||||
} else if (p_msg == "performance:profile_names") {
|
||||
Vector<StringName> monitors;
|
||||
monitors.resize(p_data.size());
|
||||
|
|
@ -811,13 +824,18 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
|
|||
if (EditorFileSystem::get_singleton()) {
|
||||
EditorFileSystem::get_singleton()->update_file(p_data[0]);
|
||||
}
|
||||
} else if (p_msg == "evaluation_return") {
|
||||
expression_evaluator->add_value(p_data);
|
||||
} else if (p_msg == "window:title") {
|
||||
ERR_FAIL_COND(p_data.size() != 1);
|
||||
emit_signal(SNAME("remote_window_title_changed"), p_data[0]);
|
||||
} else {
|
||||
int colon_index = p_msg.find_char(':');
|
||||
ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
|
||||
|
||||
bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data);
|
||||
if (!parsed) {
|
||||
WARN_PRINT("unknown message " + p_msg);
|
||||
WARN_PRINT("Unknown message: " + p_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -854,19 +872,20 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
error_tree->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_error_selected));
|
||||
error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated));
|
||||
breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));
|
||||
[[fallthrough]];
|
||||
}
|
||||
connect("started", callable_mp(expression_evaluator, &EditorExpressionEvaluator::on_start));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles)));
|
||||
|
||||
skip_breakpoints->set_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));
|
||||
copy->set_icon(get_editor_theme_icon(SNAME("ActionCopy")));
|
||||
step->set_icon(get_editor_theme_icon(SNAME("DebugStep")));
|
||||
next->set_icon(get_editor_theme_icon(SNAME("DebugNext")));
|
||||
dobreak->set_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
docontinue->set_icon(get_editor_theme_icon(SNAME("DebugContinue")));
|
||||
vmem_refresh->set_icon(get_editor_theme_icon(SNAME("Reload")));
|
||||
vmem_export->set_icon(get_editor_theme_icon(SNAME("Save")));
|
||||
skip_breakpoints->set_button_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff")));
|
||||
copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));
|
||||
step->set_button_icon(get_editor_theme_icon(SNAME("DebugStep")));
|
||||
next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext")));
|
||||
dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue")));
|
||||
vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
|
||||
vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save")));
|
||||
search->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
|
||||
reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
|
|
@ -894,37 +913,42 @@ void ScriptEditorDebugger::_notification(int p_what) {
|
|||
if (is_session_active()) {
|
||||
peer->poll();
|
||||
|
||||
if (camera_override == CameraOverride::OVERRIDE_2D) {
|
||||
Dictionary state = CanvasItemEditor::get_singleton()->get_state();
|
||||
float zoom = state["zoom"];
|
||||
Point2 offset = state["ofs"];
|
||||
Transform2D transform;
|
||||
if (camera_override == CameraOverride::OVERRIDE_EDITORS) {
|
||||
// CanvasItem Editor
|
||||
{
|
||||
Dictionary state = CanvasItemEditor::get_singleton()->get_state();
|
||||
float zoom = state["zoom"];
|
||||
Point2 offset = state["ofs"];
|
||||
Transform2D transform;
|
||||
|
||||
transform.scale_basis(Size2(zoom, zoom));
|
||||
transform.columns[2] = -offset * zoom;
|
||||
transform.scale_basis(Size2(zoom, zoom));
|
||||
transform.columns[2] = -offset * zoom;
|
||||
|
||||
Array msg;
|
||||
msg.push_back(transform);
|
||||
_put_msg("scene:override_camera_2D:transform", msg);
|
||||
|
||||
} else if (camera_override >= CameraOverride::OVERRIDE_3D_1) {
|
||||
int viewport_idx = camera_override - CameraOverride::OVERRIDE_3D_1;
|
||||
Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(viewport_idx);
|
||||
Camera3D *const cam = viewport->get_camera_3d();
|
||||
|
||||
Array msg;
|
||||
msg.push_back(cam->get_camera_transform());
|
||||
if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
|
||||
msg.push_back(false);
|
||||
msg.push_back(cam->get_size());
|
||||
} else {
|
||||
msg.push_back(true);
|
||||
msg.push_back(cam->get_fov());
|
||||
Array msg;
|
||||
msg.push_back(transform);
|
||||
_put_msg("scene:transform_camera_2d", msg);
|
||||
}
|
||||
|
||||
// Node3D Editor
|
||||
{
|
||||
Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport();
|
||||
const Camera3D *cam = viewport->get_camera_3d();
|
||||
|
||||
Array msg;
|
||||
msg.push_back(cam->get_camera_transform());
|
||||
if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
|
||||
msg.push_back(false);
|
||||
msg.push_back(cam->get_size());
|
||||
} else {
|
||||
msg.push_back(true);
|
||||
msg.push_back(cam->get_fov());
|
||||
}
|
||||
msg.push_back(cam->get_near());
|
||||
msg.push_back(cam->get_far());
|
||||
_put_msg("scene:transform_camera_3d", msg);
|
||||
}
|
||||
msg.push_back(cam->get_near());
|
||||
msg.push_back(cam->get_far());
|
||||
_put_msg("scene:override_camera_3D:transform", msg);
|
||||
}
|
||||
|
||||
if (is_breaked() && can_request_idle_draw) {
|
||||
_put_msg("servers:draw", Array());
|
||||
can_request_idle_draw = false;
|
||||
|
|
@ -1012,6 +1036,17 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) {
|
|||
_set_reason_text(TTR("Debug session started."), MESSAGE_SUCCESS);
|
||||
_update_buttons_state();
|
||||
emit_signal(SNAME("started"));
|
||||
|
||||
Array quit_keys = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
|
||||
_put_msg("scene:setup_scene", quit_keys);
|
||||
|
||||
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {
|
||||
profiler->set_profiling(true);
|
||||
}
|
||||
|
||||
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {
|
||||
visual_profiler->set_profiling(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_update_buttons_state() {
|
||||
|
|
@ -1031,6 +1066,7 @@ void ScriptEditorDebugger::_update_buttons_state() {
|
|||
for (KeyValue<uint64_t, ThreadDebugged> &I : threads_debugged) {
|
||||
threadss.push_back(&I.value);
|
||||
}
|
||||
threads->set_disabled(threadss.is_empty());
|
||||
|
||||
threadss.sort_custom<ThreadSort>();
|
||||
threads->clear();
|
||||
|
|
@ -1076,10 +1112,10 @@ void ScriptEditorDebugger::stop() {
|
|||
profiler_signature.clear();
|
||||
|
||||
profiler->set_enabled(false, false);
|
||||
profiler->set_pressed(false);
|
||||
profiler->set_profiling(false);
|
||||
|
||||
visual_profiler->set_enabled(false);
|
||||
visual_profiler->set_pressed(false);
|
||||
visual_profiler->set_profiling(false);
|
||||
|
||||
inspector->edit(nullptr);
|
||||
_update_buttons_state();
|
||||
|
|
@ -1142,6 +1178,12 @@ String ScriptEditorDebugger::get_var_value(const String &p_var) const {
|
|||
return inspector->get_stack_variable(p_var);
|
||||
}
|
||||
|
||||
void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_resources) {
|
||||
Array msg;
|
||||
msg.push_back(p_resources);
|
||||
_put_msg("scene:reload_cached_files", msg);
|
||||
}
|
||||
|
||||
int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) {
|
||||
const int *r = node_path_cache.getptr(p_path);
|
||||
if (r) {
|
||||
|
|
@ -1450,23 +1492,10 @@ CameraOverride ScriptEditorDebugger::get_camera_override() const {
|
|||
}
|
||||
|
||||
void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) {
|
||||
if (p_override == CameraOverride::OVERRIDE_2D && camera_override != CameraOverride::OVERRIDE_2D) {
|
||||
Array msg;
|
||||
msg.push_back(true);
|
||||
_put_msg("scene:override_camera_2D:set", msg);
|
||||
} else if (p_override != CameraOverride::OVERRIDE_2D && camera_override == CameraOverride::OVERRIDE_2D) {
|
||||
Array msg;
|
||||
msg.push_back(false);
|
||||
_put_msg("scene:override_camera_2D:set", msg);
|
||||
} else if (p_override >= CameraOverride::OVERRIDE_3D_1 && camera_override < CameraOverride::OVERRIDE_3D_1) {
|
||||
Array msg;
|
||||
msg.push_back(true);
|
||||
_put_msg("scene:override_camera_3D:set", msg);
|
||||
} else if (p_override < CameraOverride::OVERRIDE_3D_1 && camera_override >= CameraOverride::OVERRIDE_3D_1) {
|
||||
Array msg;
|
||||
msg.push_back(false);
|
||||
_put_msg("scene:override_camera_3D:set", msg);
|
||||
}
|
||||
Array msg;
|
||||
msg.push_back(p_override != CameraOverride::OVERRIDE_NONE);
|
||||
msg.push_back(p_override == CameraOverride::OVERRIDE_EDITORS);
|
||||
_put_msg("scene:override_cameras", msg);
|
||||
|
||||
camera_override = p_override;
|
||||
}
|
||||
|
|
@ -1757,6 +1786,8 @@ void ScriptEditorDebugger::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id")));
|
||||
ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
|
||||
ADD_SIGNAL(MethodInfo("remote_tree_updated"));
|
||||
ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::NODE_PATH, "path")));
|
||||
ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title")));
|
||||
ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));
|
||||
ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));
|
||||
ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));
|
||||
|
|
@ -1802,6 +1833,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed));
|
||||
|
||||
InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected));
|
||||
EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &ScriptEditorDebugger::_resources_reimported));
|
||||
|
||||
{ //debugger
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
|
|
@ -1822,7 +1854,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
hbc->add_child(memnew(VSeparator));
|
||||
|
||||
skip_breakpoints = memnew(Button);
|
||||
skip_breakpoints->set_theme_type_variation("FlatButton");
|
||||
skip_breakpoints->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hbc->add_child(skip_breakpoints);
|
||||
skip_breakpoints->set_tooltip_text(TTR("Skip Breakpoints"));
|
||||
skip_breakpoints->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints));
|
||||
|
|
@ -1830,7 +1862,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
hbc->add_child(memnew(VSeparator));
|
||||
|
||||
copy = memnew(Button);
|
||||
copy->set_theme_type_variation("FlatButton");
|
||||
copy->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hbc->add_child(copy);
|
||||
copy->set_tooltip_text(TTR("Copy Error"));
|
||||
copy->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_copy));
|
||||
|
|
@ -1838,14 +1870,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
hbc->add_child(memnew(VSeparator));
|
||||
|
||||
step = memnew(Button);
|
||||
step->set_theme_type_variation("FlatButton");
|
||||
step->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hbc->add_child(step);
|
||||
step->set_tooltip_text(TTR("Step Into"));
|
||||
step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into"));
|
||||
step->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_step));
|
||||
|
||||
next = memnew(Button);
|
||||
next->set_theme_type_variation("FlatButton");
|
||||
next->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hbc->add_child(next);
|
||||
next->set_tooltip_text(TTR("Step Over"));
|
||||
next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over"));
|
||||
|
|
@ -1854,14 +1886,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
hbc->add_child(memnew(VSeparator));
|
||||
|
||||
dobreak = memnew(Button);
|
||||
dobreak->set_theme_type_variation("FlatButton");
|
||||
dobreak->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hbc->add_child(dobreak);
|
||||
dobreak->set_tooltip_text(TTR("Break"));
|
||||
dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break"));
|
||||
dobreak->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_break));
|
||||
|
||||
docontinue = memnew(Button);
|
||||
docontinue->set_theme_type_variation("FlatButton");
|
||||
docontinue->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hbc->add_child(docontinue);
|
||||
docontinue->set_tooltip_text(TTR("Continue"));
|
||||
docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));
|
||||
|
|
@ -1889,16 +1921,19 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
threads->connect(SceneStringName(item_selected), callable_mp(this, &ScriptEditorDebugger::_select_thread));
|
||||
|
||||
stack_dump = memnew(Tree);
|
||||
stack_dump->set_custom_minimum_size(Size2(150, 0) * EDSCALE);
|
||||
stack_dump->set_allow_reselect(true);
|
||||
stack_dump->set_columns(1);
|
||||
stack_dump->set_column_titles_visible(true);
|
||||
stack_dump->set_column_title(0, TTR("Stack Frames"));
|
||||
stack_dump->set_hide_root(true);
|
||||
stack_dump->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
stack_dump->set_theme_type_variation("TreeSecondary");
|
||||
stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected));
|
||||
stack_vb->add_child(stack_dump);
|
||||
|
||||
VBoxContainer *inspector_vbox = memnew(VBoxContainer);
|
||||
inspector_vbox->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
|
||||
inspector_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
sc->add_child(inspector_vbox);
|
||||
|
||||
|
|
@ -1924,12 +1959,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
inspector_vbox->add_child(inspector);
|
||||
|
||||
breakpoints_tree = memnew(Tree);
|
||||
breakpoints_tree->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
|
||||
breakpoints_tree->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
breakpoints_tree->set_column_titles_visible(true);
|
||||
breakpoints_tree->set_column_title(0, TTR("Breakpoints"));
|
||||
breakpoints_tree->set_allow_reselect(true);
|
||||
breakpoints_tree->set_allow_rmb_select(true);
|
||||
breakpoints_tree->set_hide_root(true);
|
||||
breakpoints_tree->set_theme_type_variation("TreeSecondary");
|
||||
breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected));
|
||||
breakpoints_tree->create_item();
|
||||
|
||||
|
|
@ -2002,6 +2039,13 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
add_child(file_dialog);
|
||||
}
|
||||
|
||||
{ // Expression evaluator
|
||||
expression_evaluator = memnew(EditorExpressionEvaluator);
|
||||
expression_evaluator->set_name(TTR("Evaluator"));
|
||||
expression_evaluator->set_editor_debugger(this);
|
||||
tabs->add_child(expression_evaluator);
|
||||
}
|
||||
|
||||
{ //profiler
|
||||
profiler = memnew(EditorProfiler);
|
||||
profiler->set_name(TTR("Profiler"));
|
||||
|
|
@ -2036,10 +2080,10 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
|
|||
vmem_total->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
|
||||
vmem_hb->add_child(vmem_total);
|
||||
vmem_refresh = memnew(Button);
|
||||
vmem_refresh->set_theme_type_variation("FlatButton");
|
||||
vmem_refresh->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
vmem_hb->add_child(vmem_refresh);
|
||||
vmem_export = memnew(Button);
|
||||
vmem_export->set_theme_type_variation("FlatButton");
|
||||
vmem_export->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
vmem_export->set_tooltip_text(TTR("Export list to a CSV file"));
|
||||
vmem_hb->add_child(vmem_export);
|
||||
vmem_vb->add_child(vmem_hb);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class SceneDebuggerTree;
|
|||
class EditorDebuggerPlugin;
|
||||
class DebugAdapterProtocol;
|
||||
class DebugAdapterParser;
|
||||
class EditorExpressionEvaluator;
|
||||
|
||||
class ScriptEditorDebugger : public MarginContainer {
|
||||
GDCLASS(ScriptEditorDebugger, MarginContainer);
|
||||
|
|
@ -152,6 +153,7 @@ private:
|
|||
EditorProfiler *profiler = nullptr;
|
||||
EditorVisualProfiler *visual_profiler = nullptr;
|
||||
EditorPerformanceProfiler *performance_profiler = nullptr;
|
||||
EditorExpressionEvaluator *expression_evaluator = nullptr;
|
||||
|
||||
OS::ProcessID remote_pid = 0;
|
||||
bool move_to_foreground = true;
|
||||
|
|
@ -196,6 +198,8 @@ private:
|
|||
void _video_mem_request();
|
||||
void _video_mem_export();
|
||||
|
||||
void _resources_reimported(const PackedStringArray &p_resources);
|
||||
|
||||
int _get_node_path_cache(const NodePath &p_path);
|
||||
|
||||
int _get_res_path_cache(const String &p_path);
|
||||
|
|
@ -252,6 +256,8 @@ public:
|
|||
void request_remote_tree();
|
||||
const SceneDebuggerTree *get_remote_tree();
|
||||
|
||||
void request_remote_evaluate(const String &p_expression, int p_stack_frame);
|
||||
|
||||
void start(Ref<RemoteDebuggerPeer> p_peer);
|
||||
void stop();
|
||||
|
||||
|
|
|
|||
|
|
@ -237,9 +237,6 @@ void DependencyEditor::edit(const String &p_path) {
|
|||
}
|
||||
}
|
||||
|
||||
void DependencyEditor::_bind_methods() {
|
||||
}
|
||||
|
||||
DependencyEditor::DependencyEditor() {
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
vb->set_name(TTR("Dependencies"));
|
||||
|
|
@ -353,9 +350,6 @@ void DependencyEditorOwners::_file_option(int p_option) {
|
|||
}
|
||||
}
|
||||
|
||||
void DependencyEditorOwners::_bind_methods() {
|
||||
}
|
||||
|
||||
void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) {
|
||||
if (!efsd) {
|
||||
return;
|
||||
|
|
@ -477,7 +471,7 @@ void DependencyRemoveDialog::_find_localization_remaps_of_removed_files(Vector<R
|
|||
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(":");
|
||||
int splitter_pos = remapped_files[k].rfind_char(':');
|
||||
String res_path = remapped_files[k].substr(0, splitter_pos);
|
||||
if (res_path == path) {
|
||||
String locale_name = remapped_files[k].substr(splitter_pos + 1);
|
||||
|
|
@ -533,6 +527,20 @@ void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<Removed
|
|||
}
|
||||
}
|
||||
|
||||
void DependencyRemoveDialog::_show_files_to_delete_list() {
|
||||
files_to_delete_list->clear();
|
||||
|
||||
for (const String &s : dirs_to_delete) {
|
||||
String t = s.trim_prefix("res://");
|
||||
files_to_delete_list->add_item(t, Ref<Texture2D>(), false);
|
||||
}
|
||||
|
||||
for (const String &s : files_to_delete) {
|
||||
String t = s.trim_prefix("res://");
|
||||
files_to_delete_list->add_item(t, Ref<Texture2D>(), false);
|
||||
}
|
||||
}
|
||||
|
||||
void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<String> &p_files) {
|
||||
all_remove_files.clear();
|
||||
dirs_to_delete.clear();
|
||||
|
|
@ -549,21 +557,24 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<
|
|||
files_to_delete.push_back(p_files[i]);
|
||||
}
|
||||
|
||||
_show_files_to_delete_list();
|
||||
|
||||
Vector<RemovedDependency> removed_deps;
|
||||
_find_all_removed_dependencies(EditorFileSystem::get_singleton()->get_filesystem(), removed_deps);
|
||||
_find_localization_remaps_of_removed_files(removed_deps);
|
||||
removed_deps.sort();
|
||||
if (removed_deps.is_empty()) {
|
||||
owners->hide();
|
||||
vb_owners->hide();
|
||||
text->set_text(TTR("Remove the selected files from the project? (Cannot be undone.)\nDepending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently."));
|
||||
reset_size();
|
||||
popup_centered();
|
||||
} else {
|
||||
_build_removed_dependency_tree(removed_deps);
|
||||
owners->show();
|
||||
vb_owners->show();
|
||||
text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (Cannot be undone.)\nDepending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently."));
|
||||
popup_centered(Size2(500, 350));
|
||||
}
|
||||
|
||||
EditorFileSystem::get_singleton()->scan_changes();
|
||||
}
|
||||
|
||||
|
|
@ -578,43 +589,48 @@ void DependencyRemoveDialog::ok_pressed() {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < files_to_delete.size(); ++i) {
|
||||
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 (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) {
|
||||
if (file == ResourceUID::ensure_path(GLOBAL_GET("application/config/icon"))) {
|
||||
ProjectSettings::get_singleton()->set("application/config/icon", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("application/run/main_scene"))) {
|
||||
ProjectSettings::get_singleton()->set("application/run/main_scene", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("application/boot_splash/image"))) {
|
||||
ProjectSettings::get_singleton()->set("application/boot_splash/image", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
|
||||
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", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
|
||||
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", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("gui/theme/custom"))) {
|
||||
ProjectSettings::get_singleton()->set("gui/theme/custom", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
|
||||
project_settings_modified = true;
|
||||
} else if (file == ResourceUID::ensure_path(GLOBAL_GET("gui/theme/custom_font"))) {
|
||||
ProjectSettings::get_singleton()->set("gui/theme/custom_font", "");
|
||||
}
|
||||
if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
|
||||
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;
|
||||
}
|
||||
|
||||
String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/");
|
||||
const String path = OS::get_singleton()->get_resource_dir() + file.replace_first("res://", "/");
|
||||
print_verbose("Moving to trash: " + path);
|
||||
Error err = OS::get_singleton()->move_to_trash(path);
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n");
|
||||
EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + file + "\n");
|
||||
} else {
|
||||
emit_signal(SNAME("file_removed"), files_to_delete[i]);
|
||||
emit_signal(SNAME("file_removed"), file);
|
||||
}
|
||||
}
|
||||
if (project_settings_modified) {
|
||||
ProjectSettings::get_singleton()->save();
|
||||
}
|
||||
|
||||
if (dirs_to_delete.size() == 0) {
|
||||
// If we only deleted files we should only need to tell the file system about the files we touched.
|
||||
|
|
@ -667,15 +683,38 @@ DependencyRemoveDialog::DependencyRemoveDialog() {
|
|||
set_ok_button_text(TTR("Remove"));
|
||||
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
add_child(vb);
|
||||
|
||||
text = memnew(Label);
|
||||
vb->add_child(text);
|
||||
|
||||
Label *files_to_delete_label = memnew(Label);
|
||||
files_to_delete_label->set_theme_type_variation("HeaderSmall");
|
||||
files_to_delete_label->set_text(TTR("Files to be deleted:"));
|
||||
vb->add_child(files_to_delete_label);
|
||||
|
||||
files_to_delete_list = memnew(ItemList);
|
||||
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);
|
||||
vb->add_child(files_to_delete_list);
|
||||
|
||||
vb_owners = memnew(VBoxContainer);
|
||||
vb_owners->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
vb_owners->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
vb->add_child(vb_owners);
|
||||
|
||||
Label *owners_label = memnew(Label);
|
||||
owners_label->set_theme_type_variation("HeaderSmall");
|
||||
owners_label->set_text(TTR("Dependencies of files to be deleted:"));
|
||||
vb_owners->add_child(owners_label);
|
||||
|
||||
owners = memnew(Tree);
|
||||
owners->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
owners->set_hide_root(true);
|
||||
vb->add_child(owners);
|
||||
owners->set_custom_minimum_size(Size2(0, 94) * EDSCALE);
|
||||
vb_owners->add_child(owners);
|
||||
owners->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
}
|
||||
|
||||
|
|
@ -865,9 +904,6 @@ void OrphanResourcesDialog::_button_pressed(Object *p_item, int p_column, int p_
|
|||
dep_edit->edit(path);
|
||||
}
|
||||
|
||||
void OrphanResourcesDialog::_bind_methods() {
|
||||
}
|
||||
|
||||
OrphanResourcesDialog::OrphanResourcesDialog() {
|
||||
set_title(TTR("Orphan Resource Explorer"));
|
||||
delete_confirm = memnew(ConfirmationDialog);
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@
|
|||
#ifndef DEPENDENCY_EDITOR_H
|
||||
#define DEPENDENCY_EDITOR_H
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class EditorFileDialog;
|
||||
|
|
@ -60,9 +60,6 @@ class DependencyEditor : public AcceptDialog {
|
|||
|
||||
void _update_file();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void edit(const String &p_path);
|
||||
DependencyEditor();
|
||||
|
|
@ -81,7 +78,6 @@ class DependencyEditorOwners : public AcceptDialog {
|
|||
|
||||
void _fill_owners(EditorFileSystemDirectory *efsd);
|
||||
|
||||
static void _bind_methods();
|
||||
void _list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
|
||||
void _select_file(int p_idx);
|
||||
void _empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
|
||||
|
|
@ -102,6 +98,8 @@ class DependencyRemoveDialog : public ConfirmationDialog {
|
|||
|
||||
Label *text = nullptr;
|
||||
Tree *owners = nullptr;
|
||||
VBoxContainer *vb_owners = nullptr;
|
||||
ItemList *files_to_delete_list = nullptr;
|
||||
|
||||
HashMap<String, String> all_remove_files;
|
||||
Vector<String> dirs_to_delete;
|
||||
|
|
@ -126,6 +124,7 @@ class DependencyRemoveDialog : public ConfirmationDialog {
|
|||
void _find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector<RemovedDependency> &p_removed);
|
||||
void _find_localization_remaps_of_removed_files(Vector<RemovedDependency> &p_removed);
|
||||
void _build_removed_dependency_tree(const Vector<RemovedDependency> &p_removed);
|
||||
void _show_files_to_delete_list();
|
||||
|
||||
void ok_pressed() override;
|
||||
|
||||
|
|
@ -175,7 +174,6 @@ class OrphanResourcesDialog : public ConfirmationDialog {
|
|||
void _button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
|
||||
|
||||
void refresh();
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void show();
|
||||
|
|
|
|||
|
|
@ -38,33 +38,48 @@
|
|||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
|
||||
static String sanitize_input(const String &p_path) {
|
||||
String DirectoryCreateDialog::_sanitize_input(const String &p_path) const {
|
||||
String path = p_path.strip_edges();
|
||||
if (path.ends_with("/")) {
|
||||
path = path.left(path.length() - 1);
|
||||
if (mode == MODE_DIRECTORY) {
|
||||
path = path.trim_suffix("/");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
String DirectoryCreateDialog::_validate_path(const String &p_path) const {
|
||||
if (p_path.is_empty()) {
|
||||
return TTR("Folder name cannot be empty.");
|
||||
return TTR("Name cannot be empty.");
|
||||
}
|
||||
if (mode == MODE_FILE && p_path.ends_with("/")) {
|
||||
return TTR("File name can't end with /.");
|
||||
}
|
||||
|
||||
if (p_path.contains("\\") || p_path.contains(":") || p_path.contains("*") ||
|
||||
p_path.contains("|") || p_path.contains(">")) {
|
||||
return TTR("Folder name contains invalid characters.");
|
||||
}
|
||||
const PackedStringArray splits = p_path.split("/");
|
||||
for (int i = 0; i < splits.size(); i++) {
|
||||
const String &part = splits[i];
|
||||
bool is_file = mode == MODE_FILE && i == splits.size() - 1;
|
||||
|
||||
for (const String &part : p_path.split("/")) {
|
||||
if (part.is_empty()) {
|
||||
return TTR("Folder name cannot be empty.");
|
||||
if (is_file) {
|
||||
return TTR("File name cannot be empty.");
|
||||
} else {
|
||||
return TTR("Folder name cannot be empty.");
|
||||
}
|
||||
}
|
||||
if (part.ends_with(" ") || part[0] == ' ') {
|
||||
return TTR("Folder name cannot begin or end with a space.");
|
||||
if (part.contains_char('\\') || part.contains_char(':') || part.contains_char('*') ||
|
||||
part.contains_char('|') || part.contains_char('>') || part.ends_with(".") || part.ends_with(" ")) {
|
||||
if (is_file) {
|
||||
return TTR("File name contains invalid characters.");
|
||||
} else {
|
||||
return TTR("Folder name contains invalid characters.");
|
||||
}
|
||||
}
|
||||
if (part[0] == '.') {
|
||||
return TTR("Folder name cannot begin with a dot.");
|
||||
if (is_file) {
|
||||
return TTR("File name begins with a dot.");
|
||||
} else {
|
||||
return TTR("Folder name begins with a dot.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,12 +96,18 @@ String DirectoryCreateDialog::_validate_path(const String &p_path) const {
|
|||
}
|
||||
|
||||
void DirectoryCreateDialog::_on_dir_path_changed() {
|
||||
const String path = sanitize_input(dir_path->get_text());
|
||||
const String path = _sanitize_input(dir_path->get_text());
|
||||
const String error = _validate_path(path);
|
||||
|
||||
if (error.is_empty()) {
|
||||
if (path.contains("/")) {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK);
|
||||
if (path.contains_char('/')) {
|
||||
if (mode == MODE_DIRECTORY) {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in folder names will create subfolders recursively."), EditorValidationPanel::MSG_OK);
|
||||
} else {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("Using slashes in path will create the file in subfolder, creating new subfolders if necessary."), EditorValidationPanel::MSG_OK);
|
||||
}
|
||||
} else if (mode == MODE_FILE) {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, TTR("File name is valid."), EditorValidationPanel::MSG_OK);
|
||||
}
|
||||
} else {
|
||||
validation_panel->set_message(EditorValidationPanel::MSG_ID_DEFAULT, error, EditorValidationPanel::MSG_ERROR);
|
||||
|
|
@ -94,26 +115,13 @@ void DirectoryCreateDialog::_on_dir_path_changed() {
|
|||
}
|
||||
|
||||
void DirectoryCreateDialog::ok_pressed() {
|
||||
const String path = sanitize_input(dir_path->get_text());
|
||||
const String path = _sanitize_input(dir_path->get_text());
|
||||
|
||||
// The OK button should be disabled if the path is invalid, but just in case.
|
||||
const String error = _validate_path(path);
|
||||
ERR_FAIL_COND_MSG(!error.is_empty(), error);
|
||||
|
||||
Error err;
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
|
||||
err = da->change_dir(base_dir);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Cannot open directory '" + base_dir + "'.");
|
||||
|
||||
print_verbose("Making folder " + path + " in " + base_dir);
|
||||
err = da->make_dir_recursive(path);
|
||||
|
||||
if (err == OK) {
|
||||
emit_signal(SNAME("dir_created"), base_dir.path_join(path));
|
||||
} else {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Could not create folder."));
|
||||
}
|
||||
accept_callback.call(base_dir.path_join(path));
|
||||
hide();
|
||||
}
|
||||
|
||||
|
|
@ -122,28 +130,40 @@ void DirectoryCreateDialog::_post_popup() {
|
|||
dir_path->grab_focus();
|
||||
}
|
||||
|
||||
void DirectoryCreateDialog::config(const String &p_base_dir) {
|
||||
void DirectoryCreateDialog::config(const String &p_base_dir, const Callable &p_accept_callback, int p_mode, const String &p_title, const String &p_default_name) {
|
||||
set_title(p_title);
|
||||
base_dir = p_base_dir;
|
||||
label->set_text(vformat(TTR("Create new folder in %s:"), base_dir));
|
||||
dir_path->set_text("new folder");
|
||||
dir_path->select_all();
|
||||
validation_panel->update();
|
||||
}
|
||||
base_path_label->set_text(vformat(TTR("Base path: %s"), base_dir));
|
||||
accept_callback = p_accept_callback;
|
||||
mode = p_mode;
|
||||
|
||||
void DirectoryCreateDialog::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("dir_created", PropertyInfo(Variant::STRING, "path")));
|
||||
dir_path->set_text(p_default_name);
|
||||
validation_panel->update();
|
||||
|
||||
if (p_mode == MODE_FILE) {
|
||||
int extension_pos = p_default_name.rfind_char('.');
|
||||
if (extension_pos > -1) {
|
||||
dir_path->select(0, extension_pos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dir_path->select_all();
|
||||
}
|
||||
|
||||
DirectoryCreateDialog::DirectoryCreateDialog() {
|
||||
set_title(TTR("Create Folder"));
|
||||
set_min_size(Size2i(480, 0) * EDSCALE);
|
||||
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
add_child(vb);
|
||||
|
||||
label = memnew(Label);
|
||||
label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
|
||||
vb->add_child(label);
|
||||
base_path_label = memnew(Label);
|
||||
base_path_label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
|
||||
vb->add_child(base_path_label);
|
||||
|
||||
Label *name_label = memnew(Label);
|
||||
name_label->set_text(TTR("Name:"));
|
||||
name_label->set_theme_type_variation("HeaderSmall");
|
||||
vb->add_child(name_label);
|
||||
|
||||
dir_path = memnew(LineEdit);
|
||||
vb->add_child(dir_path);
|
||||
|
|
|
|||
|
|
@ -40,23 +40,31 @@ class LineEdit;
|
|||
class DirectoryCreateDialog : public ConfirmationDialog {
|
||||
GDCLASS(DirectoryCreateDialog, ConfirmationDialog);
|
||||
|
||||
String base_dir;
|
||||
public:
|
||||
enum Mode {
|
||||
MODE_FILE,
|
||||
MODE_DIRECTORY,
|
||||
};
|
||||
|
||||
Label *label = nullptr;
|
||||
private:
|
||||
String base_dir;
|
||||
Callable accept_callback;
|
||||
int mode = MODE_FILE;
|
||||
|
||||
Label *base_path_label = nullptr;
|
||||
LineEdit *dir_path = nullptr;
|
||||
EditorValidationPanel *validation_panel = nullptr;
|
||||
|
||||
String _sanitize_input(const String &p_input) const;
|
||||
String _validate_path(const String &p_path) const;
|
||||
void _on_dir_path_changed();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void ok_pressed() override;
|
||||
virtual void _post_popup() override;
|
||||
|
||||
public:
|
||||
void config(const String &p_base_dir);
|
||||
void config(const String &p_base_dir, const Callable &p_accept_callback, int p_mode, const String &p_title, const String &p_default_name = "");
|
||||
|
||||
DirectoryCreateDialog();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,12 +35,11 @@
|
|||
#include "core/core_constants.h"
|
||||
#include "core/io/compression.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/export/editor_export.h"
|
||||
#include "editor/export/editor_export_platform.h"
|
||||
#include "scene/resources/theme.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
|
|
@ -76,7 +75,7 @@ static String _translate_doc_string(const String &p_text) {
|
|||
return translated.indent(indent);
|
||||
}
|
||||
|
||||
// Comparator for constructors, based on `MetodDoc` operator.
|
||||
// Comparator for constructors, based on `MethodDoc` operator.
|
||||
struct ConstructorCompare {
|
||||
_FORCE_INLINE_ bool operator()(const DocData::MethodDoc &p_lhs, const DocData::MethodDoc &p_rhs) const {
|
||||
// Must be a constructor (i.e. assume named for the class)
|
||||
|
|
@ -277,6 +276,10 @@ static void merge_theme_properties(Vector<DocData::ThemeItemDoc> &p_to, const Ve
|
|||
// Check found entry on name and data type.
|
||||
if (to.name == from.name && to.data_type == from.data_type) {
|
||||
to.description = from.description;
|
||||
to.is_deprecated = from.is_deprecated;
|
||||
to.deprecated_message = from.deprecated_message;
|
||||
to.is_experimental = from.is_experimental;
|
||||
to.experimental_message = from.experimental_message;
|
||||
to.keywords = from.keywords;
|
||||
}
|
||||
}
|
||||
|
|
@ -571,6 +574,8 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
prop.type = retinfo.class_name;
|
||||
} else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
|
||||
prop.type = retinfo.hint_string + "[]";
|
||||
} else if (retinfo.type == Variant::DICTIONARY && retinfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) {
|
||||
prop.type = "Dictionary[" + retinfo.hint_string.replace(";", ", ") + "]";
|
||||
} else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
|
||||
prop.type = retinfo.hint_string;
|
||||
} else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
|
||||
|
|
@ -668,6 +673,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
constant.name = E;
|
||||
constant.value = itos(ClassDB::get_integer_constant(name, E));
|
||||
constant.is_value_valid = true;
|
||||
constant.type = "int";
|
||||
constant.enumeration = ClassDB::get_integer_constant_enum(name, E);
|
||||
constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration);
|
||||
c.constants.push_back(constant);
|
||||
|
|
@ -902,6 +908,24 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
|
||||
c.properties.sort();
|
||||
|
||||
List<StringName> enums;
|
||||
Variant::get_enums_for_type(Variant::Type(i), &enums);
|
||||
|
||||
for (const StringName &E : enums) {
|
||||
List<StringName> enumerations;
|
||||
Variant::get_enumerations_for_enum(Variant::Type(i), E, &enumerations);
|
||||
|
||||
for (const StringName &F : enumerations) {
|
||||
DocData::ConstantDoc constant;
|
||||
constant.name = F;
|
||||
constant.value = itos(Variant::get_enum_value(Variant::Type(i), E, F));
|
||||
constant.is_value_valid = true;
|
||||
constant.type = "int";
|
||||
constant.enumeration = E;
|
||||
c.constants.push_back(constant);
|
||||
}
|
||||
}
|
||||
|
||||
List<StringName> constants;
|
||||
Variant::get_constants_for_type(Variant::Type(i), &constants);
|
||||
|
||||
|
|
@ -911,6 +935,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
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.is_value_valid = true;
|
||||
constant.type = Variant::get_type_name(value.get_type());
|
||||
c.constants.push_back(constant);
|
||||
}
|
||||
}
|
||||
|
|
@ -928,6 +953,8 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
|
||||
DocData::ConstantDoc cd;
|
||||
cd.name = CoreConstants::get_global_constant_name(i);
|
||||
cd.type = "int";
|
||||
cd.enumeration = CoreConstants::get_global_constant_enum(i);
|
||||
cd.is_bitfield = CoreConstants::is_global_constant_bitfield(i);
|
||||
if (!CoreConstants::get_ignore_value_in_docs(i)) {
|
||||
cd.value = itos(CoreConstants::get_global_constant_value(i));
|
||||
|
|
@ -935,7 +962,6 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
} else {
|
||||
cd.is_value_valid = false;
|
||||
}
|
||||
cd.enumeration = CoreConstants::get_global_constant_enum(i);
|
||||
c.constants.push_back(cd);
|
||||
}
|
||||
|
||||
|
|
@ -975,6 +1001,8 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
DocData::ArgumentDoc ad;
|
||||
DocData::argument_doc_from_arginfo(ad, pi);
|
||||
md.return_type = ad.type;
|
||||
} else {
|
||||
md.return_type = "void";
|
||||
}
|
||||
|
||||
// Utility function's arguments.
|
||||
|
|
@ -1054,6 +1082,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
|||
cd.name = E.first;
|
||||
cd.value = E.second;
|
||||
cd.is_value_valid = true;
|
||||
cd.type = Variant::get_type_name(E.second.get_type());
|
||||
c.constants.push_back(cd);
|
||||
}
|
||||
|
||||
|
|
@ -1420,6 +1449,14 @@ Error DocTools::_load(Ref<XMLParser> parser) {
|
|||
prop2.type = parser->get_named_attribute_value("type");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("data_type"), ERR_FILE_CORRUPT);
|
||||
prop2.data_type = parser->get_named_attribute_value("data_type");
|
||||
if (parser->has_attribute("deprecated")) {
|
||||
prop2.is_deprecated = true;
|
||||
prop2.deprecated_message = parser->get_named_attribute_value("deprecated");
|
||||
}
|
||||
if (parser->has_attribute("experimental")) {
|
||||
prop2.is_experimental = true;
|
||||
prop2.experimental_message = parser->get_named_attribute_value("experimental");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
prop2.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
|
|
@ -1738,6 +1775,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
|
|||
if (!ti.default_value.is_empty()) {
|
||||
additional_attributes += String(" default=\"") + ti.default_value.xml_escape(true) + "\"";
|
||||
}
|
||||
if (ti.is_deprecated) {
|
||||
additional_attributes += " deprecated=\"" + ti.deprecated_message.xml_escape(true) + "\"";
|
||||
}
|
||||
if (ti.is_experimental) {
|
||||
additional_attributes += " experimental=\"" + ti.experimental_message.xml_escape(true) + "\"";
|
||||
}
|
||||
if (!ti.keywords.is_empty()) {
|
||||
additional_attributes += String(" keywords=\"") + ti.keywords.xml_escape(true) + "\"";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,16 +33,15 @@
|
|||
#include "core/authors.gen.h"
|
||||
#include "core/donors.gen.h"
|
||||
#include "core/license.gen.h"
|
||||
#include "core/os/time.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_version_button.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/resources/style_box.h"
|
||||
|
||||
// The metadata key used to store and retrieve the version text to copy to the clipboard.
|
||||
const String EditorAbout::META_TEXT_TO_COPY = "text_to_copy";
|
||||
|
||||
void EditorAbout::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
|
|
@ -81,10 +80,6 @@ void EditorAbout::_license_tree_selected() {
|
|||
_tpl_text->set_text(selected->get_metadata(0));
|
||||
}
|
||||
|
||||
void EditorAbout::_version_button_pressed() {
|
||||
DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
|
||||
}
|
||||
|
||||
void EditorAbout::_item_with_website_selected(int p_id, ItemList *p_il) {
|
||||
const String website = p_il->get_item_metadata(p_id);
|
||||
if (!website.is_empty()) {
|
||||
|
|
@ -198,25 +193,7 @@ EditorAbout::EditorAbout() {
|
|||
Control *v_spacer = memnew(Control);
|
||||
version_info_vbc->add_child(v_spacer);
|
||||
|
||||
version_btn = memnew(LinkButton);
|
||||
String hash = String(VERSION_HASH);
|
||||
if (hash.length() != 0) {
|
||||
hash = " " + vformat("[%s]", hash.left(9));
|
||||
}
|
||||
version_btn->set_text(VERSION_FULL_NAME + hash);
|
||||
// Set the text to copy in metadata as it slightly differs from the button's text.
|
||||
version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
|
||||
version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
|
||||
String build_date;
|
||||
if (VERSION_TIMESTAMP > 0) {
|
||||
build_date = Time::get_singleton()->get_datetime_string_from_unix_time(VERSION_TIMESTAMP, true) + " UTC";
|
||||
} else {
|
||||
build_date = TTR("(unknown)");
|
||||
}
|
||||
version_btn->set_tooltip_text(vformat(TTR("Git commit date: %s\nClick to copy the version number."), build_date));
|
||||
|
||||
version_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorAbout::_version_button_pressed));
|
||||
version_info_vbc->add_child(version_btn);
|
||||
version_info_vbc->add_child(memnew(EditorVersionButton(EditorVersionButton::FORMAT_WITH_NAME_AND_BUILD)));
|
||||
|
||||
Label *about_text = memnew(Label);
|
||||
about_text->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
|
||||
|
|
|
|||
|
|
@ -33,12 +33,8 @@
|
|||
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/link_button.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
|
|
@ -49,16 +45,12 @@
|
|||
class EditorAbout : public AcceptDialog {
|
||||
GDCLASS(EditorAbout, AcceptDialog);
|
||||
|
||||
static const String META_TEXT_TO_COPY;
|
||||
|
||||
private:
|
||||
void _license_tree_selected();
|
||||
void _version_button_pressed();
|
||||
void _item_with_website_selected(int p_id, ItemList *p_il);
|
||||
void _item_list_resized(ItemList *p_il);
|
||||
ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], int p_single_column_flags = 0, bool p_allow_website = false);
|
||||
|
||||
LinkButton *version_btn = nullptr;
|
||||
Tree *_tpl_tree = nullptr;
|
||||
RichTextLabel *license_text_label = nullptr;
|
||||
RichTextLabel *_tpl_text = nullptr;
|
||||
|
|
|
|||
|
|
@ -130,14 +130,14 @@ void EditorAssetInstaller::open_asset(const String &p_path, bool p_autoskip_topl
|
|||
|
||||
// Create intermediate directories if they aren't reported by unzip.
|
||||
// We are only interested in subfolders, so skip the root slash.
|
||||
int separator = source_name.find("/", 1);
|
||||
int separator = source_name.find_char('/', 1);
|
||||
while (separator != -1) {
|
||||
String dir_name = source_name.substr(0, separator + 1);
|
||||
if (!dir_name.is_empty() && !asset_files.has(dir_name)) {
|
||||
asset_files.insert(dir_name);
|
||||
}
|
||||
|
||||
separator = source_name.find("/", separator + 1);
|
||||
separator = source_name.find_char('/', separator + 1);
|
||||
}
|
||||
|
||||
if (!source_name.is_empty() && !asset_files.has(source_name)) {
|
||||
|
|
@ -214,7 +214,7 @@ void EditorAssetInstaller::_rebuild_source_tree() {
|
|||
|
||||
TreeItem *parent_item;
|
||||
|
||||
int separator = path.rfind("/");
|
||||
int separator = path.rfind_char('/');
|
||||
if (separator == -1) {
|
||||
parent_item = root;
|
||||
} else {
|
||||
|
|
@ -313,7 +313,7 @@ void EditorAssetInstaller::_rebuild_destination_tree() {
|
|||
|
||||
TreeItem *parent_item;
|
||||
|
||||
int separator = path.rfind("/");
|
||||
int separator = path.rfind_char('/');
|
||||
if (separator == -1) {
|
||||
parent_item = root;
|
||||
} else {
|
||||
|
|
@ -411,9 +411,9 @@ void EditorAssetInstaller::_toggle_source_tree(bool p_visible, bool p_scroll_to_
|
|||
show_source_files_button->set_pressed_no_signal(p_visible); // To keep in sync if triggered by something else.
|
||||
|
||||
if (p_visible) {
|
||||
show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
} else {
|
||||
show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
}
|
||||
|
||||
if (p_visible && p_scroll_to_error && first_file_conflict) {
|
||||
|
|
@ -597,9 +597,9 @@ void EditorAssetInstaller::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
if (show_source_files_button->is_pressed()) {
|
||||
show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
} else {
|
||||
show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
}
|
||||
asset_conflicts_link->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
|
||||
|
||||
|
|
@ -659,9 +659,6 @@ void EditorAssetInstaller::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorAssetInstaller::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorAssetInstaller::EditorAssetInstaller() {
|
||||
VBoxContainer *vb = memnew(VBoxContainer);
|
||||
add_child(vb);
|
||||
|
|
@ -688,7 +685,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
|
|||
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."));
|
||||
remapping_tools->add_child(show_source_files_button);
|
||||
show_source_files_button->connect("toggled", callable_mp(this, &EditorAssetInstaller::_toggle_source_tree).bind(false));
|
||||
show_source_files_button->connect(SceneStringName(toggled), callable_mp(this, &EditorAssetInstaller::_toggle_source_tree).bind(false));
|
||||
|
||||
Button *target_dir_button = memnew(Button);
|
||||
target_dir_button->set_text(TTR("Change Install Folder"));
|
||||
|
|
@ -701,7 +698,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
|
|||
skip_toplevel_check = memnew(CheckBox);
|
||||
skip_toplevel_check->set_text(TTR("Ignore asset root"));
|
||||
skip_toplevel_check->set_tooltip_text(TTR("Ignore the root directory when extracting files."));
|
||||
skip_toplevel_check->connect("toggled", callable_mp(this, &EditorAssetInstaller::_set_skip_toplevel));
|
||||
skip_toplevel_check->connect(SceneStringName(toggled), callable_mp(this, &EditorAssetInstaller::_set_skip_toplevel));
|
||||
remapping_tools->add_child(skip_toplevel_check);
|
||||
|
||||
remapping_tools->add_spacer();
|
||||
|
|
@ -738,6 +735,7 @@ EditorAssetInstaller::EditorAssetInstaller() {
|
|||
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));
|
||||
source_tree->set_theme_type_variation("TreeSecondary");
|
||||
source_tree_vb->add_child(source_tree);
|
||||
|
||||
VBoxContainer *destination_tree_vb = memnew(VBoxContainer);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ class EditorAssetInstaller : public ConfirmationDialog {
|
|||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void open_asset(const String &p_path, bool p_autoskip_toplevel = false);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/math/geometry_2d.h"
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/math/vector2i.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
|
||||
void EditorAtlasPacker::chart_pack(Vector<Chart> &charts, int &r_width, int &r_height, int p_atlas_max_size, int p_cell_resolution) {
|
||||
int divide_by = MIN(64, p_cell_resolution);
|
||||
|
|
|
|||
|
|
@ -31,11 +31,9 @@
|
|||
#ifndef EDITOR_ATLAS_PACKER_H
|
||||
#define EDITOR_ATLAS_PACKER_H
|
||||
|
||||
#include "core/math/vector2.h"
|
||||
#include "core/math/vector2i.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
|
||||
struct Vector2;
|
||||
struct Vector2i;
|
||||
|
||||
class EditorAtlasPacker {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -91,19 +91,28 @@ void EditorAudioBus::_notification(int p_what) {
|
|||
Color mute_color = EditorThemeManager::is_dark_theme() ? Color(1.0, 0.16, 0.16) : Color(2.35, 1.03, 1.03);
|
||||
Color bypass_color = EditorThemeManager::is_dark_theme() ? Color(0.13, 0.8, 1.0) : Color(1.03, 2.04, 2.35);
|
||||
float darkening_factor = EditorThemeManager::is_dark_theme() ? 0.15 : 0.65;
|
||||
Color solo_color_darkened = solo_color.darkened(darkening_factor);
|
||||
Color mute_color_darkened = mute_color.darkened(darkening_factor);
|
||||
Color bypass_color_darkened = bypass_color.darkened(darkening_factor);
|
||||
|
||||
Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color.darkened(darkening_factor));
|
||||
Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color.darkened(darkening_factor));
|
||||
Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color.darkened(darkening_factor));
|
||||
Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color_darkened);
|
||||
Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color_darkened);
|
||||
Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color_darkened);
|
||||
Ref<StyleBoxFlat>(solo->get_theme_stylebox("hover_pressed"))->set_border_color(solo_color_darkened);
|
||||
Ref<StyleBoxFlat>(mute->get_theme_stylebox("hover_pressed"))->set_border_color(mute_color_darkened);
|
||||
Ref<StyleBoxFlat>(bypass->get_theme_stylebox("hover_pressed"))->set_border_color(bypass_color_darkened);
|
||||
|
||||
solo->set_icon(get_editor_theme_icon(SNAME("AudioBusSolo")));
|
||||
solo->set_button_icon(get_editor_theme_icon(SNAME("AudioBusSolo")));
|
||||
solo->add_theme_color_override("icon_pressed_color", solo_color);
|
||||
mute->set_icon(get_editor_theme_icon(SNAME("AudioBusMute")));
|
||||
solo->add_theme_color_override("icon_hover_pressed_color", solo_color_darkened);
|
||||
mute->set_button_icon(get_editor_theme_icon(SNAME("AudioBusMute")));
|
||||
mute->add_theme_color_override("icon_pressed_color", mute_color);
|
||||
bypass->set_icon(get_editor_theme_icon(SNAME("AudioBusBypass")));
|
||||
mute->add_theme_color_override("icon_hover_pressed_color", mute_color_darkened);
|
||||
bypass->set_button_icon(get_editor_theme_icon(SNAME("AudioBusBypass")));
|
||||
bypass->add_theme_color_override("icon_pressed_color", bypass_color);
|
||||
bypass->add_theme_color_override("icon_hover_pressed_color", bypass_color_darkened);
|
||||
|
||||
bus_options->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
|
||||
bus_options->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
|
||||
|
||||
audio_value_preview_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), SNAME("TooltipLabel")));
|
||||
audio_value_preview_label->add_theme_color_override("font_shadow_color", get_theme_color(SNAME("font_shadow_color"), SNAME("TooltipLabel")));
|
||||
|
|
@ -127,7 +136,7 @@ void EditorAudioBus::_notification(int p_what) {
|
|||
} else if (has_focus()) {
|
||||
draw_style_box(get_theme_stylebox(SNAME("focus"), SNAME("Button")), Rect2(Vector2(), get_size()));
|
||||
} else {
|
||||
draw_style_box(get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer")), Rect2(Vector2(), get_size()));
|
||||
draw_style_box(get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)), Rect2(Vector2(), get_size()));
|
||||
}
|
||||
|
||||
if (get_index() != 0 && hovering_drop) {
|
||||
|
|
@ -657,6 +666,7 @@ Variant EditorAudioBus::get_drag_data_fw(const Point2 &p_point, Control *p_from)
|
|||
|
||||
Label *l = memnew(Label);
|
||||
l->set_text(item->get_text(0));
|
||||
l->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
effects->set_drag_preview(l);
|
||||
|
||||
return fxd;
|
||||
|
|
@ -805,28 +815,28 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
|
|||
set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
track_name = memnew(LineEdit);
|
||||
track_name->connect("text_submitted", callable_mp(this, &EditorAudioBus::_name_changed));
|
||||
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);
|
||||
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
vb->add_child(hbc);
|
||||
solo = memnew(Button);
|
||||
solo->set_theme_type_variation("FlatButton");
|
||||
solo->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
solo->set_toggle_mode(true);
|
||||
solo->set_tooltip_text(TTR("Solo"));
|
||||
solo->set_focus_mode(FOCUS_NONE);
|
||||
solo->connect(SceneStringName(pressed), callable_mp(this, &EditorAudioBus::_solo_toggled));
|
||||
hbc->add_child(solo);
|
||||
mute = memnew(Button);
|
||||
mute->set_theme_type_variation("FlatButton");
|
||||
mute->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
mute->set_toggle_mode(true);
|
||||
mute->set_tooltip_text(TTR("Mute"));
|
||||
mute->set_focus_mode(FOCUS_NONE);
|
||||
mute->connect(SceneStringName(pressed), callable_mp(this, &EditorAudioBus::_mute_toggled));
|
||||
hbc->add_child(mute);
|
||||
bypass = memnew(Button);
|
||||
bypass->set_theme_type_variation("FlatButton");
|
||||
bypass->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
bypass->set_toggle_mode(true);
|
||||
bypass->set_tooltip_text(TTR("Bypass"));
|
||||
bypass->set_focus_mode(FOCUS_NONE);
|
||||
|
|
@ -839,14 +849,19 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
|
|||
Control *child = Object::cast_to<Control>(hbc->get_child(i));
|
||||
child->begin_bulk_theme_override();
|
||||
child->add_theme_style_override(CoreStringName(normal), sbempty);
|
||||
child->add_theme_style_override("hover", sbempty);
|
||||
child->add_theme_style_override(SceneStringName(hover), sbempty);
|
||||
child->add_theme_style_override("hover_mirrored", sbempty);
|
||||
child->add_theme_style_override("focus", sbempty);
|
||||
child->add_theme_style_override("focus_mirrored", sbempty);
|
||||
|
||||
Ref<StyleBoxFlat> sbflat = memnew(StyleBoxFlat);
|
||||
sbflat->set_content_margin_all(0);
|
||||
sbflat->set_bg_color(Color(1, 1, 1, 0));
|
||||
sbflat->set_border_width(Side::SIDE_BOTTOM, Math::round(3 * EDSCALE));
|
||||
child->add_theme_style_override(SceneStringName(pressed), sbflat);
|
||||
child->add_theme_style_override("pressed_mirrored", sbflat);
|
||||
child->add_theme_style_override("hover_pressed", sbflat);
|
||||
child->add_theme_style_override("hover_pressed_mirrored", sbflat);
|
||||
|
||||
child->end_bulk_theme_override();
|
||||
}
|
||||
|
|
@ -929,6 +944,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
|
|||
hb->add_child(scale);
|
||||
|
||||
effects = memnew(Tree);
|
||||
effects->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
effects->set_hide_root(true);
|
||||
effects->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
|
||||
effects->set_hide_folding(true);
|
||||
|
|
@ -943,6 +959,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
|
|||
effects->set_allow_rmb_select(true);
|
||||
effects->set_focus_mode(FOCUS_CLICK);
|
||||
effects->set_allow_reselect(true);
|
||||
effects->set_theme_type_variation("TreeSecondary");
|
||||
effects->connect(SceneStringName(gui_input), callable_mp(this, &EditorAudioBus::_effects_gui_input));
|
||||
|
||||
send = memnew(OptionButton);
|
||||
|
|
@ -954,6 +971,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
|
|||
set_focus_mode(FOCUS_CLICK);
|
||||
|
||||
effect_options = memnew(PopupMenu);
|
||||
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;
|
||||
|
|
@ -977,8 +995,8 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
|
|||
hbc->add_child(bus_options);
|
||||
|
||||
bus_popup = bus_options->get_popup();
|
||||
bus_popup->add_shortcut(ED_SHORTCUT("audio_bus_editor/duplicate_selected_bus", TTR("Duplicate Bus"), KeyModifierMask::CMD_OR_CTRL | Key::D));
|
||||
bus_popup->add_shortcut(ED_SHORTCUT("audio_bus_editor/delete_selected_bus", TTR("Delete Bus"), Key::KEY_DELETE));
|
||||
bus_popup->add_shortcut(ED_SHORTCUT("audio_bus_editor/duplicate_selected_bus", TTRC("Duplicate Bus"), KeyModifierMask::CMD_OR_CTRL | Key::D));
|
||||
bus_popup->add_shortcut(ED_SHORTCUT("audio_bus_editor/delete_selected_bus", TTRC("Delete Bus"), Key::KEY_DELETE));
|
||||
bus_popup->set_item_disabled(1, is_master);
|
||||
bus_popup->add_item(TTR("Reset Volume"));
|
||||
bus_popup->connect("index_pressed", callable_mp(this, &EditorAudioBus::_bus_popup_pressed));
|
||||
|
|
@ -1064,7 +1082,7 @@ void EditorAudioBuses::_rebuild_buses() {
|
|||
|
||||
EditorAudioBuses *EditorAudioBuses::register_editor() {
|
||||
EditorAudioBuses *audio_buses = memnew(EditorAudioBuses);
|
||||
EditorNode::get_bottom_panel()->add_item(TTR("Audio"), audio_buses, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_audio_bottom_panel", TTR("Toggle Audio Bottom Panel"), KeyModifierMask::ALT | Key::A));
|
||||
EditorNode::get_bottom_panel()->add_item(TTR("Audio"), audio_buses, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_audio_bottom_panel", TTRC("Toggle Audio Bottom Panel"), KeyModifierMask::ALT | Key::A));
|
||||
return audio_buses;
|
||||
}
|
||||
|
||||
|
|
@ -1247,41 +1265,11 @@ void EditorAudioBuses::_load_layout() {
|
|||
}
|
||||
|
||||
void EditorAudioBuses::_load_default_layout() {
|
||||
String layout_path = GLOBAL_GET("audio/buses/default_bus_layout");
|
||||
|
||||
Ref<AudioBusLayout> state;
|
||||
if (ResourceLoader::exists(layout_path)) {
|
||||
state = ResourceLoader::load(layout_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
|
||||
}
|
||||
if (state.is_null()) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR("There is no '%s' file."), layout_path));
|
||||
return;
|
||||
}
|
||||
|
||||
edited_path = layout_path;
|
||||
file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file());
|
||||
AudioServer::get_singleton()->set_bus_layout(state);
|
||||
_rebuild_buses();
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred();
|
||||
open_layout(GLOBAL_GET("audio/buses/default_bus_layout"));
|
||||
}
|
||||
|
||||
void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
|
||||
if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_OPEN_FILE) {
|
||||
Ref<AudioBusLayout> state = ResourceLoader::load(p_string, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
|
||||
if (state.is_null()) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout."));
|
||||
return;
|
||||
}
|
||||
|
||||
edited_path = p_string;
|
||||
file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
|
||||
AudioServer::get_singleton()->set_bus_layout(state);
|
||||
_rebuild_buses();
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred();
|
||||
|
||||
} else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
|
||||
if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
|
||||
if (new_layout) {
|
||||
Ref<AudioBusLayout> empty_state;
|
||||
empty_state.instantiate();
|
||||
|
|
@ -1289,18 +1277,12 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) {
|
|||
}
|
||||
|
||||
Error err = ResourceSaver::save(AudioServer::get_singleton()->generate_bus_layout(), p_string);
|
||||
|
||||
if (err != OK) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR("Error saving file: %s"), p_string));
|
||||
return;
|
||||
}
|
||||
|
||||
edited_path = p_string;
|
||||
file->set_text(String(TTR("Layout:")) + " " + p_string.get_file());
|
||||
_rebuild_buses();
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred();
|
||||
}
|
||||
open_layout(p_string);
|
||||
}
|
||||
|
||||
void EditorAudioBuses::_bind_methods() {
|
||||
|
|
@ -1312,9 +1294,10 @@ EditorAudioBuses::EditorAudioBuses() {
|
|||
top_hb = memnew(HBoxContainer);
|
||||
add_child(top_hb);
|
||||
|
||||
edited_path = ResourceUID::ensure_path(GLOBAL_GET("audio/buses/default_bus_layout"));
|
||||
|
||||
file = memnew(Label);
|
||||
String layout_path = GLOBAL_GET("audio/buses/default_bus_layout");
|
||||
file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file());
|
||||
file->set_text(vformat("%s %s", TTR("Layout:"), edited_path.get_file()));
|
||||
file->set_clip_text(true);
|
||||
file->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
top_hb->add_child(file);
|
||||
|
|
@ -1368,8 +1351,6 @@ EditorAudioBuses::EditorAudioBuses() {
|
|||
|
||||
set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
edited_path = GLOBAL_GET("audio/buses/default_bus_layout");
|
||||
|
||||
file_dialog = memnew(EditorFileDialog);
|
||||
List<String> ext;
|
||||
ResourceLoader::get_recognized_extensions_for_type("AudioBusLayout", &ext);
|
||||
|
|
@ -1387,17 +1368,24 @@ EditorAudioBuses::EditorAudioBuses() {
|
|||
void EditorAudioBuses::open_layout(const String &p_path) {
|
||||
EditorNode::get_bottom_panel()->make_item_visible(this);
|
||||
|
||||
Ref<AudioBusLayout> state = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
|
||||
if (state.is_null()) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Invalid file, not an audio bus layout."));
|
||||
const String path = ResourceUID::ensure_path(p_path);
|
||||
|
||||
if (!ResourceLoader::exists(path)) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR(R"(Can't open audio bus layout: "%s" doesn't exist.)"), path));
|
||||
return;
|
||||
}
|
||||
|
||||
edited_path = p_path;
|
||||
file->set_text(p_path.get_file());
|
||||
Ref<AudioBusLayout> state = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
|
||||
if (state.is_null()) {
|
||||
EditorNode::get_singleton()->show_warning(vformat(TTR(R"(Can't open audio bus layout: "%s" is not a valid audio bus layout.)"), path));
|
||||
return;
|
||||
}
|
||||
|
||||
edited_path = path;
|
||||
file->set_text(vformat("%s %s", TTR("Layout:"), path.get_file()));
|
||||
AudioServer::get_singleton()->set_bus_layout(state);
|
||||
_rebuild_buses();
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
EditorUndoRedoManager::get_singleton()->clear_history(EditorUndoRedoManager::GLOBAL_HISTORY);
|
||||
callable_mp(this, &EditorAudioBuses::_select_layout).call_deferred();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ class AudioBusesEditorPlugin : public EditorPlugin {
|
|||
EditorAudioBuses *audio_bus_editor = nullptr;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return "SampleLibrary"; }
|
||||
virtual String get_plugin_name() const override { return "SampleLibrary"; }
|
||||
bool has_main_screen() const override { return false; }
|
||||
virtual void edit(Object *p_node) override;
|
||||
virtual bool handles(Object *p_node) const override;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
#include "editor/filesystem_dock.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/project_settings_editor.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
|
|
@ -55,12 +54,12 @@ void EditorAutoloadSettings::_notification(int p_what) {
|
|||
file_dialog->add_filter("*." + E);
|
||||
}
|
||||
|
||||
browse_button->set_icon(get_editor_theme_icon(SNAME("Folder")));
|
||||
browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
browse_button->set_icon(get_editor_theme_icon(SNAME("Folder")));
|
||||
add_autoload->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
|
||||
add_autoload->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
|
|
@ -88,14 +87,9 @@ void EditorAutoloadSettings::_notification(int p_what) {
|
|||
}
|
||||
|
||||
bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, String *r_error) {
|
||||
if (!p_name.is_valid_identifier()) {
|
||||
if (!p_name.is_valid_unicode_identifier()) {
|
||||
if (r_error) {
|
||||
*r_error = TTR("Invalid name.") + " ";
|
||||
if (p_name.size() > 0 && p_name.left(1).is_numeric()) {
|
||||
*r_error += TTR("Cannot begin with a digit.");
|
||||
} else {
|
||||
*r_error += TTR("Valid characters:") + " a-z, A-Z, 0-9 or _";
|
||||
}
|
||||
*r_error = TTR("Invalid name.") + " " + TTR("Must be a valid Unicode identifier.");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -117,14 +111,12 @@ bool EditorAutoloadSettings::_autoload_name_is_valid(const String &p_name, Strin
|
|||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (Variant::get_type_name(Variant::Type(i)) == p_name) {
|
||||
if (r_error) {
|
||||
*r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing built-in type name.");
|
||||
}
|
||||
|
||||
return false;
|
||||
if (Variant::get_type_by_name(p_name) < Variant::VARIANT_MAX) {
|
||||
if (r_error) {
|
||||
*r_error = TTR("Invalid name.") + " " + TTR("Must not collide with an existing built-in type name.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
|
||||
|
|
@ -403,7 +395,7 @@ Node *EditorAutoloadSettings::_create_autoload(const String &p_path) {
|
|||
scn.instantiate();
|
||||
scn->set_path(p_path);
|
||||
scn->reload_from_file();
|
||||
ERR_FAIL_COND_V_MSG(!scn.is_valid(), nullptr, vformat("Failed to create an autoload, can't load from path: %s.", p_path));
|
||||
ERR_FAIL_COND_V_MSG(scn.is_null(), nullptr, vformat("Failed to create an autoload, can't load from path: %s.", p_path));
|
||||
|
||||
if (scn.is_valid()) {
|
||||
n = scn->instantiate();
|
||||
|
|
@ -457,7 +449,9 @@ void EditorAutoloadSettings::init_autoloads() {
|
|||
|
||||
for (const AutoloadInfo &info : autoload_cache) {
|
||||
if (info.node && info.in_editor) {
|
||||
callable_mp((Node *)get_tree()->get_root(), &Node::add_child).call_deferred(info.node, false, Node::INTERNAL_MODE_DISABLED);
|
||||
// It's important to add the node without deferring because code in plugins or tool scripts
|
||||
// could use the autoload node when they are enabled.
|
||||
get_tree()->get_root()->add_child(info.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -642,6 +636,7 @@ Variant EditorAutoloadSettings::get_drag_data_fw(const Point2 &p_point, Control
|
|||
for (int i = 0; i < max_size; i++) {
|
||||
Label *label = memnew(Label(autoloads[i]));
|
||||
label->set_self_modulate(Color(1, 1, 1, Math::lerp(1, 0, float(i) / PREVIEW_LIST_MAX_SIZE)));
|
||||
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
|
||||
preview->add_child(label);
|
||||
}
|
||||
|
|
@ -720,6 +715,15 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &
|
|||
Dictionary drop_data = p_data;
|
||||
PackedStringArray autoloads = drop_data["autoloads"];
|
||||
|
||||
// Store the initial order of the autoloads for comparison.
|
||||
Vector<int> initial_orders;
|
||||
initial_orders.resize(autoload_cache.size());
|
||||
int idx = 0;
|
||||
for (const AutoloadInfo &F : autoload_cache) {
|
||||
initial_orders.write[idx++] = F.order;
|
||||
}
|
||||
|
||||
// Perform the drag-and-drop operation.
|
||||
Vector<int> orders;
|
||||
orders.resize(autoload_cache.size());
|
||||
|
||||
|
|
@ -739,10 +743,14 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &
|
|||
}
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
|
||||
idx = 0;
|
||||
for (const AutoloadInfo &F : autoload_cache) {
|
||||
orders.write[i++] = F.order;
|
||||
orders.write[idx++] = F.order;
|
||||
}
|
||||
|
||||
// If the order didn't change, we shouldn't create undo/redo actions.
|
||||
if (orders == initial_orders) {
|
||||
return;
|
||||
}
|
||||
|
||||
orders.sort();
|
||||
|
|
@ -751,10 +759,9 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant &
|
|||
|
||||
undo_redo->create_action(TTR("Rearrange Autoloads"));
|
||||
|
||||
i = 0;
|
||||
|
||||
idx = 0;
|
||||
for (const AutoloadInfo &F : autoload_cache) {
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", "autoload/" + F.name, orders[i++]);
|
||||
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", "autoload/" + F.name, orders[idx++]);
|
||||
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", "autoload/" + F.name, F.order);
|
||||
}
|
||||
|
||||
|
|
@ -923,7 +930,7 @@ EditorAutoloadSettings::EditorAutoloadSettings() {
|
|||
|
||||
autoload_add_name = memnew(LineEdit);
|
||||
autoload_add_name->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
autoload_add_name->connect("text_submitted", callable_mp(this, &EditorAutoloadSettings::_autoload_text_submitted));
|
||||
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));
|
||||
hbc->add_child(autoload_add_name);
|
||||
|
||||
|
|
@ -977,7 +984,7 @@ EditorAutoloadSettings::~EditorAutoloadSettings() {
|
|||
|
||||
void EditorAutoloadSettings::_set_autoload_add_path(const String &p_text) {
|
||||
autoload_add_path->set_text(p_text);
|
||||
autoload_add_path->emit_signal(SNAME("text_submitted"), p_text);
|
||||
autoload_add_path->emit_signal(SceneStringName(text_submitted), p_text);
|
||||
}
|
||||
|
||||
void EditorAutoloadSettings::_browse_autoload_add_path() {
|
||||
|
|
|
|||
|
|
@ -30,16 +30,15 @@
|
|||
|
||||
#include "editor_build_profile.h"
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/json.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_property_name_processor.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/separator.h"
|
||||
|
||||
const char *EditorBuildProfile::build_option_identifiers[BUILD_OPTION_MAX] = {
|
||||
// This maps to SCons build options.
|
||||
|
|
@ -73,7 +72,7 @@ const bool EditorBuildProfile::build_option_disabled_by_default[BUILD_OPTION_MAX
|
|||
false, // TEXT_SERVER_COMPLEX
|
||||
false, // DYNAMIC_FONTS
|
||||
false, // WOFF2_FONTS
|
||||
false, // GRPAHITE_FONTS
|
||||
false, // GRAPHITE_FONTS
|
||||
false, // MSDFGEN
|
||||
};
|
||||
|
||||
|
|
@ -91,7 +90,7 @@ const bool EditorBuildProfile::build_option_disable_values[BUILD_OPTION_MAX] = {
|
|||
false, // TEXT_SERVER_COMPLEX
|
||||
false, // DYNAMIC_FONTS
|
||||
false, // WOFF2_FONTS
|
||||
false, // GRPAHITE_FONTS
|
||||
false, // GRAPHITE_FONTS
|
||||
false, // MSDFGEN
|
||||
};
|
||||
|
||||
|
|
@ -108,7 +107,7 @@ const EditorBuildProfile::BuildOptionCategory EditorBuildProfile::build_option_c
|
|||
BUILD_OPTION_CATEGORY_TEXT_SERVER, // TEXT_SERVER_COMPLEX
|
||||
BUILD_OPTION_CATEGORY_TEXT_SERVER, // DYNAMIC_FONTS
|
||||
BUILD_OPTION_CATEGORY_TEXT_SERVER, // WOFF2_FONTS
|
||||
BUILD_OPTION_CATEGORY_TEXT_SERVER, // GRPAHITE_FONTS
|
||||
BUILD_OPTION_CATEGORY_TEXT_SERVER, // GRAPHITE_FONTS
|
||||
BUILD_OPTION_CATEGORY_TEXT_SERVER, // MSDFGEN
|
||||
};
|
||||
|
||||
|
|
@ -345,7 +344,7 @@ void EditorBuildProfile::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(BUILD_OPTION_TEXT_SERVER_ADVANCED);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_DYNAMIC_FONTS);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_WOFF2_FONTS);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_GRPAHITE_FONTS);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_GRAPHITE_FONTS);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_MSDFGEN);
|
||||
BIND_ENUM_CONSTANT(BUILD_OPTION_MAX);
|
||||
|
||||
|
|
@ -649,7 +648,7 @@ void EditorBuildProfileManager::_class_list_item_selected() {
|
|||
}
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
description_bit->parse_symbol("class|" + md.operator String() + "|");
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
String build_option_description = EditorBuildProfile::get_build_option_description(EditorBuildProfile::BuildOption((int)md));
|
||||
|
|
@ -670,7 +669,7 @@ void EditorBuildProfileManager::_class_list_item_edited() {
|
|||
bool checked = item->is_checked(0);
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
String class_selected = md;
|
||||
edited->set_disable_class(class_selected, !checked);
|
||||
_update_edited_profile();
|
||||
|
|
@ -691,7 +690,7 @@ void EditorBuildProfileManager::_class_list_item_collapsed(Object *p_item) {
|
|||
}
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() != Variant::STRING && md.get_type() != Variant::STRING_NAME) {
|
||||
if (!md.is_string()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -706,7 +705,7 @@ void EditorBuildProfileManager::_update_edited_profile() {
|
|||
|
||||
if (class_list->get_selected()) {
|
||||
Variant md = class_list->get_selected()->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
class_selected = md;
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
build_option_selected = md;
|
||||
|
|
|
|||
|
|
@ -31,13 +31,9 @@
|
|||
#ifndef EDITOR_BUILD_PROFILE_H
|
||||
#define EDITOR_BUILD_PROFILE_H
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "editor/editor_help.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
class EditorBuildProfile : public RefCounted {
|
||||
|
|
@ -57,7 +53,7 @@ public:
|
|||
BUILD_OPTION_TEXT_SERVER_ADVANCED,
|
||||
BUILD_OPTION_DYNAMIC_FONTS,
|
||||
BUILD_OPTION_WOFF2_FONTS,
|
||||
BUILD_OPTION_GRPAHITE_FONTS,
|
||||
BUILD_OPTION_GRAPHITE_FONTS,
|
||||
BUILD_OPTION_MSDFGEN,
|
||||
BUILD_OPTION_MAX,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -183,18 +183,13 @@ void EditorCommandPalette::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorCommandPalette::_sbox_input(const Ref<InputEvent> &p_ie) {
|
||||
Ref<InputEventKey> k = p_ie;
|
||||
if (k.is_valid()) {
|
||||
switch (k->get_keycode()) {
|
||||
case Key::UP:
|
||||
case Key::DOWN:
|
||||
case Key::PAGEUP:
|
||||
case Key::PAGEDOWN: {
|
||||
search_options->gui_input(k);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
void EditorCommandPalette::_sbox_input(const Ref<InputEvent> &p_event) {
|
||||
// Redirect navigational key events to the tree.
|
||||
Ref<InputEventKey> key = p_event;
|
||||
if (key.is_valid()) {
|
||||
if (key->is_action("ui_up", true) || key->is_action("ui_down", true) || key->is_action("ui_page_up") || key->is_action("ui_page_down")) {
|
||||
search_options->gui_input(key);
|
||||
command_search_box->accept_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -212,6 +207,7 @@ void EditorCommandPalette::open_popup() {
|
|||
if (was_showed) {
|
||||
popup(prev_rect);
|
||||
} else {
|
||||
_update_command_search(String());
|
||||
popup_centered_clamped(Size2(600, 440) * EDSCALE, 0.8f);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define EDITOR_COMMAND_PALETTE_H
|
||||
|
||||
#include "core/input/shortcut.h"
|
||||
#include "core/os/thread_safe.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
|
|
@ -80,7 +79,7 @@ class EditorCommandPalette : public ConfirmationDialog {
|
|||
|
||||
void _update_command_search(const String &search_text);
|
||||
float _score_path(const String &p_search, const String &p_path);
|
||||
void _sbox_input(const Ref<InputEvent> &p_ie);
|
||||
void _sbox_input(const Ref<InputEvent> &p_event);
|
||||
void _confirmed();
|
||||
void _add_command(String p_command_name, String p_key_name, Callable p_binded_action, String p_shortcut_text = "None");
|
||||
void _save_history() const;
|
||||
|
|
|
|||
|
|
@ -33,14 +33,13 @@
|
|||
#include "core/config/project_settings.h"
|
||||
#include "core/extension/gdextension_manager.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/multi_node_edit.h"
|
||||
#include "editor/plugins/editor_context_menu_plugin.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/property_utils.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
void EditorSelectionHistory::cleanup_history() {
|
||||
|
|
@ -48,7 +47,7 @@ void EditorSelectionHistory::cleanup_history() {
|
|||
bool fail = false;
|
||||
|
||||
for (int j = 0; j < history[i].path.size(); j++) {
|
||||
if (!history[i].path[j].ref.is_null()) {
|
||||
if (history[i].path[j].ref.is_valid()) {
|
||||
// If the node is a MultiNodeEdit node, examine it and see if anything is missing from it.
|
||||
Ref<MultiNodeEdit> multi_node_edit = history[i].path[j].ref;
|
||||
if (multi_node_edit.is_valid()) {
|
||||
|
|
@ -278,7 +277,7 @@ Vector<EditorPlugin *> EditorData::get_handling_sub_editors(Object *p_object) {
|
|||
|
||||
EditorPlugin *EditorData::get_editor_by_name(const String &p_name) {
|
||||
for (int i = editor_plugins.size() - 1; i > -1; i--) {
|
||||
if (editor_plugins[i]->get_name() == p_name) {
|
||||
if (editor_plugins[i]->get_plugin_name() == p_name) {
|
||||
return editor_plugins[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -317,7 +316,7 @@ Dictionary EditorData::get_editor_plugin_states() const {
|
|||
if (state.is_empty()) {
|
||||
continue;
|
||||
}
|
||||
metadata[editor_plugins[i]->get_name()] = state;
|
||||
metadata[editor_plugins[i]->get_plugin_name()] = state;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
|
|
@ -345,7 +344,7 @@ void EditorData::set_editor_plugin_states(const Dictionary &p_states) {
|
|||
String name = E->get();
|
||||
int idx = -1;
|
||||
for (int i = 0; i < editor_plugins.size(); i++) {
|
||||
if (editor_plugins[i]->get_name() == name) {
|
||||
if (editor_plugins[i]->get_plugin_name() == name) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -538,14 +537,18 @@ Variant EditorData::instantiate_custom_type(const String &p_type, const String &
|
|||
if (get_custom_types()[p_inherits][i].name == p_type) {
|
||||
Ref<Script> script = get_custom_types()[p_inherits][i].script;
|
||||
|
||||
Variant ob = ClassDB::instantiate(p_inherits);
|
||||
ERR_FAIL_COND_V(!ob, Variant());
|
||||
// Store in a variant to initialize the refcount if needed.
|
||||
Variant v = ClassDB::instantiate(p_inherits);
|
||||
ERR_FAIL_COND_V(!v, Variant());
|
||||
Object *ob = v;
|
||||
|
||||
Node *n = Object::cast_to<Node>(ob);
|
||||
if (n) {
|
||||
n->set_name(p_type);
|
||||
}
|
||||
((Object *)ob)->set_script(script);
|
||||
return ob;
|
||||
PropertyUtils::assign_custom_type_script(ob, script);
|
||||
ob->set_script(script);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -839,9 +842,9 @@ Ref<Script> EditorData::get_scene_root_script(int p_idx) const {
|
|||
return Ref<Script>();
|
||||
}
|
||||
Ref<Script> s = edited_scene[p_idx].root->get_script();
|
||||
if (!s.is_valid() && edited_scene[p_idx].root->get_child_count()) {
|
||||
if (s.is_null() && edited_scene[p_idx].root->get_child_count()) {
|
||||
Node *n = edited_scene[p_idx].root->get_child(0);
|
||||
while (!s.is_valid() && n && n->get_scene_file_path().is_empty()) {
|
||||
while (s.is_null() && n && n->get_scene_file_path().is_empty()) {
|
||||
s = n->get_script();
|
||||
n = n->get_parent();
|
||||
}
|
||||
|
|
@ -984,30 +987,18 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
|
|||
return true;
|
||||
}
|
||||
|
||||
StringName EditorData::script_class_get_base(const String &p_class) const {
|
||||
Ref<Script> script = script_class_load_script(p_class);
|
||||
if (script.is_null()) {
|
||||
return StringName();
|
||||
}
|
||||
|
||||
Ref<Script> base_script = script->get_base_script();
|
||||
if (base_script.is_null()) {
|
||||
return ScriptServer::get_global_class_base(p_class);
|
||||
}
|
||||
|
||||
return script->get_language()->get_global_class_name(base_script->get_path());
|
||||
}
|
||||
|
||||
Variant EditorData::script_class_instance(const String &p_class) {
|
||||
if (ScriptServer::is_global_class(p_class)) {
|
||||
Ref<Script> script = script_class_load_script(p_class);
|
||||
if (script.is_valid()) {
|
||||
// Store in a variant to initialize the refcount if needed.
|
||||
Variant obj = ClassDB::instantiate(script->get_instance_base_type());
|
||||
if (obj) {
|
||||
obj.operator Object *()->set_script(script);
|
||||
Variant v = ClassDB::instantiate(script->get_instance_base_type());
|
||||
if (v) {
|
||||
Object *obj = v;
|
||||
PropertyUtils::assign_custom_type_script(obj, script);
|
||||
obj->set_script(script);
|
||||
}
|
||||
return obj;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
return Variant();
|
||||
|
|
@ -1026,22 +1017,25 @@ void EditorData::script_class_set_icon_path(const String &p_class, const String
|
|||
_script_class_icon_paths[p_class] = p_icon_path;
|
||||
}
|
||||
|
||||
String EditorData::script_class_get_icon_path(const String &p_class) const {
|
||||
if (!ScriptServer::is_global_class(p_class)) {
|
||||
return String();
|
||||
}
|
||||
|
||||
String EditorData::script_class_get_icon_path(const String &p_class, bool *r_valid) const {
|
||||
String current = p_class;
|
||||
String ret = _script_class_icon_paths[current];
|
||||
while (ret.is_empty()) {
|
||||
current = script_class_get_base(current);
|
||||
while (true) {
|
||||
if (!ScriptServer::is_global_class(current)) {
|
||||
// If the classnames chain has a native class ancestor, we're done with success.
|
||||
if (r_valid) {
|
||||
*r_valid = ClassDB::class_exists(current);
|
||||
}
|
||||
return String();
|
||||
}
|
||||
ret = _script_class_icon_paths.has(current) ? _script_class_icon_paths[current] : String();
|
||||
HashMap<StringName, String>::ConstIterator E = _script_class_icon_paths.find(current);
|
||||
if ((bool)E && !E->value.is_empty()) {
|
||||
if (r_valid) {
|
||||
*r_valid = true;
|
||||
}
|
||||
return E->value;
|
||||
}
|
||||
current = ScriptServer::get_global_class_base(current);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
StringName EditorData::script_class_get_name(const String &p_path) const {
|
||||
|
|
@ -1052,24 +1046,23 @@ void EditorData::script_class_set_name(const String &p_path, const StringName &p
|
|||
_script_class_file_to_path[p_path] = p_class;
|
||||
}
|
||||
|
||||
void EditorData::script_class_save_icon_paths() {
|
||||
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
|
||||
|
||||
Dictionary d;
|
||||
for (const KeyValue<StringName, String> &E : _script_class_icon_paths) {
|
||||
if (ScriptServer::is_global_class(E.key)) {
|
||||
d[E.key] = E.value;
|
||||
}
|
||||
void EditorData::script_class_save_global_classes() {
|
||||
List<StringName> global_classes;
|
||||
ScriptServer::get_global_class_list(&global_classes);
|
||||
Array array_classes;
|
||||
for (const StringName &class_name : global_classes) {
|
||||
Dictionary d;
|
||||
String *icon = _script_class_icon_paths.getptr(class_name);
|
||||
d["class"] = class_name;
|
||||
d["language"] = ScriptServer::get_global_class_language(class_name);
|
||||
d["path"] = ScriptServer::get_global_class_path(class_name);
|
||||
d["base"] = ScriptServer::get_global_class_base(class_name);
|
||||
d["icon"] = icon ? *icon : String();
|
||||
d["is_abstract"] = ScriptServer::is_global_class_abstract(class_name);
|
||||
d["is_tool"] = ScriptServer::is_global_class_tool(class_name);
|
||||
array_classes.push_back(d);
|
||||
}
|
||||
|
||||
for (int i = 0; i < script_classes.size(); i++) {
|
||||
Dictionary d2 = script_classes[i];
|
||||
if (!d2.has("class")) {
|
||||
continue;
|
||||
}
|
||||
d2["icon"] = d.get(d2["class"], "");
|
||||
}
|
||||
ProjectSettings::get_singleton()->store_global_class_list(script_classes);
|
||||
ProjectSettings::get_singleton()->store_global_class_list(array_classes);
|
||||
}
|
||||
|
||||
void EditorData::script_class_load_icon_paths() {
|
||||
|
|
@ -1126,28 +1119,42 @@ Ref<Texture2D> EditorData::_load_script_icon(const String &p_path) const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
|
||||
Ref<Texture2D> EditorData::get_script_icon(const String &p_script_path) {
|
||||
// Take from the local cache, if available.
|
||||
if (_script_icon_cache.has(p_script)) {
|
||||
if (_script_icon_cache.has(p_script_path)) {
|
||||
// Can be an empty value if we can't resolve any icon for this script.
|
||||
// An empty value is still cached to avoid unnecessary attempts at resolving it again.
|
||||
return _script_icon_cache[p_script];
|
||||
return _script_icon_cache[p_script_path];
|
||||
}
|
||||
|
||||
Ref<Script> base_scr = p_script;
|
||||
// Fast path in case the whole hierarchy is made of global classes.
|
||||
StringName class_name = script_class_get_name(p_script_path);
|
||||
{
|
||||
if (class_name != StringName()) {
|
||||
bool icon_valid = false;
|
||||
String icon_path = script_class_get_icon_path(class_name, &icon_valid);
|
||||
if (icon_valid) {
|
||||
Ref<Texture2D> icon = _load_script_icon(icon_path);
|
||||
_script_icon_cache[p_script_path] = icon;
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Script> base_scr = ResourceLoader::load(p_script_path, "Script");
|
||||
while (base_scr.is_valid()) {
|
||||
// Check for scripted classes.
|
||||
String icon_path;
|
||||
StringName class_name = script_class_get_name(base_scr->get_path());
|
||||
if (base_scr->is_built_in() || class_name == StringName()) {
|
||||
StringName base_class_name = script_class_get_name(base_scr->get_path());
|
||||
if (base_scr->is_built_in() || base_class_name == StringName()) {
|
||||
icon_path = base_scr->get_class_icon_path();
|
||||
} else {
|
||||
icon_path = script_class_get_icon_path(class_name);
|
||||
icon_path = script_class_get_icon_path(base_class_name);
|
||||
}
|
||||
|
||||
Ref<Texture2D> icon = _load_script_icon(icon_path);
|
||||
if (icon.is_valid()) {
|
||||
_script_icon_cache[p_script] = icon;
|
||||
_script_icon_cache[p_script_path] = icon;
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
|
@ -1155,7 +1162,7 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
|
|||
// TODO: Should probably be deprecated in 4.x
|
||||
const EditorData::CustomType *ctype = get_custom_type_by_path(base_scr->get_path());
|
||||
if (ctype && ctype->icon.is_valid()) {
|
||||
_script_icon_cache[p_script] = ctype->icon;
|
||||
_script_icon_cache[p_script_path] = ctype->icon;
|
||||
return ctype->icon;
|
||||
}
|
||||
|
||||
|
|
@ -1163,21 +1170,16 @@ Ref<Texture2D> EditorData::get_script_icon(const Ref<Script> &p_script) {
|
|||
base_scr = base_scr->get_base_script();
|
||||
}
|
||||
|
||||
// No custom icon was found in the inheritance chain, so check the base
|
||||
// class of the script instead.
|
||||
String base_type;
|
||||
p_script->get_language()->get_global_class_name(p_script->get_path(), &base_type);
|
||||
|
||||
// Check if the base type is an extension-defined type.
|
||||
Ref<Texture2D> ext_icon = extension_class_get_icon(base_type);
|
||||
Ref<Texture2D> ext_icon = extension_class_get_icon(class_name);
|
||||
if (ext_icon.is_valid()) {
|
||||
_script_icon_cache[p_script] = ext_icon;
|
||||
_script_icon_cache[p_script_path] = ext_icon;
|
||||
return ext_icon;
|
||||
}
|
||||
|
||||
// If no icon found, cache it as null.
|
||||
_script_icon_cache[p_script] = Ref<Texture>();
|
||||
return nullptr;
|
||||
_script_icon_cache[p_script_path] = Ref<Texture2D>();
|
||||
return Ref<Texture2D>();
|
||||
}
|
||||
|
||||
void EditorData::clear_script_icon_cache() {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
class ConfigFile;
|
||||
class EditorPlugin;
|
||||
class EditorUndoRedoManager;
|
||||
class PopupMenu;
|
||||
|
||||
/**
|
||||
* Stores the history of objects which have been selected for editing in the Editor & the Inspector.
|
||||
|
|
@ -146,7 +147,7 @@ private:
|
|||
|
||||
HashMap<StringName, String> _script_class_icon_paths;
|
||||
HashMap<String, StringName> _script_class_file_to_path;
|
||||
HashMap<Ref<Script>, Ref<Texture>> _script_icon_cache;
|
||||
HashMap<String, Ref<Texture>> _script_icon_cache;
|
||||
|
||||
Ref<Texture2D> _load_script_icon(const String &p_path) const;
|
||||
|
||||
|
|
@ -240,7 +241,6 @@ public:
|
|||
void notify_scene_saved(const String &p_path);
|
||||
|
||||
bool script_class_is_parent(const String &p_class, const String &p_inherits);
|
||||
StringName script_class_get_base(const String &p_class) const;
|
||||
Variant script_class_instance(const String &p_class);
|
||||
|
||||
Ref<Script> script_class_load_script(const String &p_class) const;
|
||||
|
|
@ -248,15 +248,15 @@ public:
|
|||
StringName script_class_get_name(const String &p_path) const;
|
||||
void script_class_set_name(const String &p_path, const StringName &p_class);
|
||||
|
||||
String script_class_get_icon_path(const String &p_class) const;
|
||||
String script_class_get_icon_path(const String &p_class, bool *r_valid = nullptr) const;
|
||||
void script_class_set_icon_path(const String &p_class, const String &p_icon_path);
|
||||
void script_class_clear_icon_paths() { _script_class_icon_paths.clear(); }
|
||||
void script_class_save_icon_paths();
|
||||
void script_class_save_global_classes();
|
||||
void script_class_load_icon_paths();
|
||||
|
||||
Ref<Texture2D> extension_class_get_icon(const String &p_class) const;
|
||||
|
||||
Ref<Texture2D> get_script_icon(const Ref<Script> &p_script);
|
||||
Ref<Texture2D> get_script_icon(const String &p_script_path);
|
||||
void clear_script_icon_cache();
|
||||
|
||||
EditorData();
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/filesystem_dock.h"
|
||||
#include "editor/gui/editor_bottom_panel.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/window_wrapper.h"
|
||||
|
|
@ -174,6 +173,9 @@ void EditorDockManager::_update_docks_menu() {
|
|||
docks_menu_docks.clear();
|
||||
int id = 0;
|
||||
for (const KeyValue<Control *, DockInfo> &dock : all_docks) {
|
||||
if (!dock.value.enabled) {
|
||||
continue;
|
||||
}
|
||||
if (dock.value.shortcut.is_valid()) {
|
||||
docks_menu->add_shortcut(dock.value.shortcut, id);
|
||||
docks_menu->set_item_text(id, dock.value.title);
|
||||
|
|
@ -184,8 +186,10 @@ void EditorDockManager::_update_docks_menu() {
|
|||
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);
|
||||
docks_menu->set_item_tooltip(id, vformat(TTR("Open the %s dock."), dock.value.title));
|
||||
} else {
|
||||
docks_menu->set_item_tooltip(id, vformat(TTR("Focus on the %s dock."), dock.value.title));
|
||||
}
|
||||
docks_menu->set_item_disabled(id, !dock.value.enabled);
|
||||
docks_menu_docks.push_back(dock.key);
|
||||
id++;
|
||||
}
|
||||
|
|
@ -475,6 +479,8 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
|
|||
Array bottom_docks_dump;
|
||||
Array closed_docks_dump;
|
||||
for (const KeyValue<Control *, DockInfo> &d : all_docks) {
|
||||
d.key->call(SNAME("_save_layout_to_config"), p_layout, p_section);
|
||||
|
||||
if (!d.value.at_bottom && d.value.open && (!d.value.previous_at_bottom || !d.value.dock_window)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -509,18 +515,16 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
|
|||
}
|
||||
|
||||
for (int i = 0; i < hsplits.size(); i++) {
|
||||
p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset());
|
||||
p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), int(hsplits[i]->get_split_offset() / EDSCALE));
|
||||
}
|
||||
|
||||
FileSystemDock::get_singleton()->save_layout_to_config(p_layout, p_section);
|
||||
}
|
||||
|
||||
void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
|
||||
void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section, bool p_first_load) {
|
||||
Dictionary floating_docks_dump = p_layout->get_value(p_section, "dock_floating", Dictionary());
|
||||
Array dock_bottom = p_layout->get_value(p_section, "dock_bottom", Array());
|
||||
Array closed_docks = p_layout->get_value(p_section, "dock_closed", Array());
|
||||
|
||||
bool restore_window_on_load = EDITOR_GET("interface/multi_window/restore_windows_on_load");
|
||||
bool allow_floating_docks = EditorNode::get_singleton()->is_multi_window_enabled() && (!p_first_load || EDITOR_GET("interface/multi_window/restore_windows_on_load"));
|
||||
|
||||
// Store the docks by name for easy lookup.
|
||||
HashMap<String, Control *> dock_map;
|
||||
|
|
@ -543,13 +547,14 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
|
|||
continue;
|
||||
}
|
||||
Control *dock = dock_map[name];
|
||||
dock->call(SNAME("_load_layout_from_config"), p_layout, p_section);
|
||||
|
||||
if (!all_docks[dock].enabled) {
|
||||
// Don't open disabled docks.
|
||||
continue;
|
||||
}
|
||||
bool at_bottom = false;
|
||||
if (restore_window_on_load && floating_docks_dump.has(name)) {
|
||||
if (allow_floating_docks && floating_docks_dump.has(name)) {
|
||||
all_docks[dock].previous_at_bottom = dock_bottom.has(name);
|
||||
_restore_dock_to_saved_window(dock, floating_docks_dump[name]);
|
||||
} else if (dock_bottom.has(name)) {
|
||||
|
|
@ -605,11 +610,8 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
|
|||
continue;
|
||||
}
|
||||
int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
|
||||
hsplits[i]->set_split_offset(ofs);
|
||||
hsplits[i]->set_split_offset(ofs * EDSCALE);
|
||||
}
|
||||
|
||||
FileSystemDock::get_singleton()->load_layout_from_config(p_layout, p_section);
|
||||
|
||||
_update_docks_menu();
|
||||
}
|
||||
|
||||
|
|
@ -712,7 +714,7 @@ void EditorDockManager::focus_dock(Control *p_dock) {
|
|||
}
|
||||
|
||||
if (all_docks[p_dock].at_bottom) {
|
||||
EditorNode::get_bottom_panel()->make_item_visible(p_dock);
|
||||
EditorNode::get_bottom_panel()->make_item_visible(p_dock, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -852,21 +854,21 @@ void DockContextPopup::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
if (make_float_button) {
|
||||
make_float_button->set_icon(get_editor_theme_icon(SNAME("MakeFloating")));
|
||||
make_float_button->set_button_icon(get_editor_theme_icon(SNAME("MakeFloating")));
|
||||
}
|
||||
if (is_layout_rtl()) {
|
||||
tab_move_left_button->set_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
tab_move_right_button->set_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
tab_move_left_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
tab_move_right_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
tab_move_left_button->set_tooltip_text(TTR("Move this dock right one tab."));
|
||||
tab_move_right_button->set_tooltip_text(TTR("Move this dock left one tab."));
|
||||
} else {
|
||||
tab_move_left_button->set_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
tab_move_right_button->set_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
tab_move_left_button->set_button_icon(get_editor_theme_icon(SNAME("Back")));
|
||||
tab_move_right_button->set_button_icon(get_editor_theme_icon(SNAME("Forward")));
|
||||
tab_move_left_button->set_tooltip_text(TTR("Move this dock left one tab."));
|
||||
tab_move_right_button->set_tooltip_text(TTR("Move this dock right one tab."));
|
||||
}
|
||||
dock_to_bottom_button->set_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide")));
|
||||
close_button->set_icon(get_editor_theme_icon(SNAME("Close")));
|
||||
dock_to_bottom_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide")));
|
||||
close_button->set_button_icon(get_editor_theme_icon(SNAME("Close")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ public:
|
|||
PopupMenu *get_docks_menu();
|
||||
|
||||
void save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) const;
|
||||
void load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section);
|
||||
void load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section, bool p_first_load = false);
|
||||
|
||||
void set_dock_enabled(Control *p_dock, bool p_enabled);
|
||||
void close_dock(Control *p_dock);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/separator.h"
|
||||
|
||||
const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
|
||||
TTRC("3D Editor"),
|
||||
|
|
@ -49,6 +50,7 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
|
|||
TTRC("FileSystem Dock"),
|
||||
TTRC("Import Dock"),
|
||||
TTRC("History Dock"),
|
||||
TTRC("Game View"),
|
||||
};
|
||||
|
||||
const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = {
|
||||
|
|
@ -60,6 +62,7 @@ const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = {
|
|||
TTRC("Allows to browse the local file system via a dedicated dock."),
|
||||
TTRC("Allows to configure import settings for individual assets. Requires the FileSystem dock to function."),
|
||||
TTRC("Provides an overview of the editor's and each scene's undo history."),
|
||||
TTRC("Provides tools for selecting and debugging nodes at runtime."),
|
||||
};
|
||||
|
||||
const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
|
||||
|
|
@ -71,6 +74,7 @@ const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
|
|||
"filesystem_dock",
|
||||
"import_dock",
|
||||
"history_dock",
|
||||
"game",
|
||||
};
|
||||
|
||||
void EditorFeatureProfile::set_disable_class(const StringName &p_class, bool p_disabled) {
|
||||
|
|
@ -307,6 +311,7 @@ void EditorFeatureProfile::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(FEATURE_FILESYSTEM_DOCK);
|
||||
BIND_ENUM_CONSTANT(FEATURE_IMPORT_DOCK);
|
||||
BIND_ENUM_CONSTANT(FEATURE_HISTORY_DOCK);
|
||||
BIND_ENUM_CONSTANT(FEATURE_GAME);
|
||||
BIND_ENUM_CONSTANT(FEATURE_MAX);
|
||||
}
|
||||
|
||||
|
|
@ -468,7 +473,7 @@ void EditorFeatureProfileManager::_erase_selected_profile() {
|
|||
|
||||
void EditorFeatureProfileManager::_create_new_profile() {
|
||||
String name = new_profile_name->get_text().strip_edges();
|
||||
if (!name.is_valid_filename() || name.contains(".")) {
|
||||
if (!name.is_valid_filename() || name.contains_char('.')) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Profile must be a valid filename and must not contain '.'"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -558,7 +563,7 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
|
|||
}
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
description_bit->parse_symbol("class|" + md.operator String() + "|");
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
String feature_description = EditorFeatureProfile::get_feature_description(EditorFeatureProfile::Feature((int)md));
|
||||
|
|
@ -643,7 +648,7 @@ void EditorFeatureProfileManager::_class_list_item_edited() {
|
|||
bool checked = item->is_checked(0);
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
String class_selected = md;
|
||||
edited->set_disable_class(class_selected, !checked);
|
||||
_save_and_update();
|
||||
|
|
@ -666,7 +671,7 @@ void EditorFeatureProfileManager::_class_list_item_collapsed(Object *p_item) {
|
|||
}
|
||||
|
||||
Variant md = item->get_metadata(0);
|
||||
if (md.get_type() != Variant::STRING && md.get_type() != Variant::STRING_NAME) {
|
||||
if (!md.is_string()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -686,7 +691,7 @@ void EditorFeatureProfileManager::_property_item_edited() {
|
|||
}
|
||||
|
||||
Variant md = class_item->get_metadata(0);
|
||||
if (md.get_type() != Variant::STRING && md.get_type() != Variant::STRING_NAME) {
|
||||
if (!md.is_string()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -699,7 +704,7 @@ void EditorFeatureProfileManager::_property_item_edited() {
|
|||
bool checked = item->is_checked(0);
|
||||
|
||||
md = item->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
String property_selected = md;
|
||||
edited->set_disable_class_property(class_name, property_selected, !checked);
|
||||
_save_and_update();
|
||||
|
|
@ -732,7 +737,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
|
|||
|
||||
if (class_list->get_selected()) {
|
||||
Variant md = class_list->get_selected()->get_metadata(0);
|
||||
if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
|
||||
if (md.is_string()) {
|
||||
class_selected = md;
|
||||
} else if (md.get_type() == Variant::INT) {
|
||||
feature_selected = md;
|
||||
|
|
@ -985,6 +990,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
|
|||
class_list->connect("cell_selected", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_selected));
|
||||
class_list->connect("item_edited", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_edited), CONNECT_DEFERRED);
|
||||
class_list->connect("item_collapsed", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_collapsed));
|
||||
class_list->set_theme_type_variation("TreeSecondary");
|
||||
// It will be displayed once the user creates or chooses a profile.
|
||||
class_list_vbc->hide();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,10 @@
|
|||
#ifndef EDITOR_FEATURE_PROFILE_H
|
||||
#define EDITOR_FEATURE_PROFILE_H
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "editor/editor_help.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
|
|
@ -55,6 +53,7 @@ public:
|
|||
FEATURE_FILESYSTEM_DOCK,
|
||||
FEATURE_IMPORT_DOCK,
|
||||
FEATURE_HISTORY_DOCK,
|
||||
FEATURE_GAME,
|
||||
FEATURE_MAX
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -32,6 +32,8 @@
|
|||
#define EDITOR_FILE_SYSTEM_H
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/os/thread_safe.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
|
|
@ -58,15 +60,21 @@ class EditorFileSystemDirectory : public Object {
|
|||
ResourceUID::ID uid = ResourceUID::INVALID_ID;
|
||||
uint64_t modified_time = 0;
|
||||
uint64_t import_modified_time = 0;
|
||||
String import_md5;
|
||||
Vector<String> import_dest_paths;
|
||||
bool import_valid = false;
|
||||
String import_group_file;
|
||||
Vector<String> deps;
|
||||
bool verified = false; //used for checking changes
|
||||
// These are for script resources only.
|
||||
String script_class_name;
|
||||
String script_class_extends;
|
||||
String script_class_icon_path;
|
||||
String icon_path;
|
||||
// This is for script resources only.
|
||||
struct ScriptClassInfo {
|
||||
String name;
|
||||
String extends;
|
||||
String icon_path;
|
||||
bool is_abstract = false;
|
||||
bool is_tool = false;
|
||||
};
|
||||
ScriptClassInfo class_info;
|
||||
};
|
||||
|
||||
Vector<FileInfo *> files;
|
||||
|
|
@ -110,9 +118,9 @@ class EditorFileSystemImportFormatSupportQuery : public RefCounted {
|
|||
GDCLASS(EditorFileSystemImportFormatSupportQuery, RefCounted);
|
||||
|
||||
protected:
|
||||
GDVIRTUAL0RC(bool, _is_active)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_file_extensions)
|
||||
GDVIRTUAL0RC(bool, _query)
|
||||
GDVIRTUAL0RC_REQUIRED(bool, _is_active)
|
||||
GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_file_extensions)
|
||||
GDVIRTUAL0RC_REQUIRED(bool, _query)
|
||||
static void _bind_methods() {
|
||||
GDVIRTUAL_BIND(_is_active);
|
||||
GDVIRTUAL_BIND(_get_file_extensions);
|
||||
|
|
@ -122,17 +130,17 @@ protected:
|
|||
public:
|
||||
virtual bool is_active() const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_REQUIRED_CALL(_is_active, ret);
|
||||
GDVIRTUAL_CALL(_is_active, ret);
|
||||
return ret;
|
||||
}
|
||||
virtual Vector<String> get_file_extensions() const {
|
||||
Vector<String> ret;
|
||||
GDVIRTUAL_REQUIRED_CALL(_get_file_extensions, ret);
|
||||
GDVIRTUAL_CALL(_get_file_extensions, ret);
|
||||
return ret;
|
||||
}
|
||||
virtual bool query() {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_REQUIRED_CALL(_query, ret);
|
||||
GDVIRTUAL_CALL(_query, ret);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
|
@ -174,8 +182,9 @@ class EditorFileSystem : public Node {
|
|||
static void _thread_func(void *_userdata);
|
||||
|
||||
EditorFileSystemDirectory *new_filesystem = nullptr;
|
||||
ScannedDirectory *first_scan_root_dir = nullptr;
|
||||
static ScannedDirectory *first_scan_root_dir;
|
||||
|
||||
bool filesystem_changed_queued = false;
|
||||
bool scanning = false;
|
||||
bool importing = false;
|
||||
bool first_scan = true;
|
||||
|
|
@ -183,11 +192,16 @@ class EditorFileSystem : public Node {
|
|||
float scan_total;
|
||||
String filesystem_settings_version_for_import;
|
||||
bool revalidate_import_files = false;
|
||||
int nb_files_total = 0;
|
||||
static int nb_files_total;
|
||||
|
||||
void _notify_filesystem_changed();
|
||||
void _scan_filesystem();
|
||||
void _first_scan_filesystem();
|
||||
void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names);
|
||||
void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, List<String> &p_gdextension_extensions, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions);
|
||||
|
||||
static void _scan_for_uid_directory(const ScannedDirectory *p_scan_dir, const HashSet<String> &p_import_extensions);
|
||||
|
||||
static void _load_first_scan_root_dir();
|
||||
|
||||
HashSet<String> late_update_files;
|
||||
|
||||
|
|
@ -197,19 +211,21 @@ class EditorFileSystem : public Node {
|
|||
|
||||
static EditorFileSystem *singleton;
|
||||
|
||||
using ScriptClassInfo = EditorFileSystemDirectory::FileInfo::ScriptClassInfo;
|
||||
|
||||
/* Used for reading the filesystem cache file */
|
||||
struct FileCache {
|
||||
String type;
|
||||
StringName type;
|
||||
String resource_script_class;
|
||||
ResourceUID::ID uid = ResourceUID::INVALID_ID;
|
||||
uint64_t modification_time = 0;
|
||||
uint64_t import_modification_time = 0;
|
||||
String import_md5;
|
||||
Vector<String> import_dest_paths;
|
||||
Vector<String> deps;
|
||||
bool import_valid = false;
|
||||
String import_group_file;
|
||||
String script_class_name;
|
||||
String script_class_extends;
|
||||
String script_class_icon_path;
|
||||
ScriptClassInfo class_info;
|
||||
};
|
||||
|
||||
HashMap<String, FileCache> file_cache;
|
||||
|
|
@ -222,22 +238,29 @@ class EditorFileSystem : public Node {
|
|||
void increment();
|
||||
};
|
||||
|
||||
struct DirectoryComparator {
|
||||
bool operator()(const EditorFileSystemDirectory *p_a, const EditorFileSystemDirectory *p_b) const {
|
||||
return p_a->name.filenocasecmp_to(p_b->name) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
void _save_filesystem_cache();
|
||||
void _save_filesystem_cache(EditorFileSystemDirectory *p_dir, Ref<FileAccess> p_file);
|
||||
|
||||
bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const;
|
||||
|
||||
void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress);
|
||||
void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive = true);
|
||||
|
||||
void _delete_internal_files(const String &p_file);
|
||||
int _insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir);
|
||||
|
||||
HashSet<String> textfile_extensions;
|
||||
HashSet<String> other_file_extensions;
|
||||
HashSet<String> valid_extensions;
|
||||
HashSet<String> import_extensions;
|
||||
|
||||
int _scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da);
|
||||
void _process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress);
|
||||
static int _scan_new_dir(ScannedDirectory *p_dir, Ref<DirAccess> &da);
|
||||
void _process_file_system(const ScannedDirectory *p_scan_dir, EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, HashSet<String> *p_processed_files);
|
||||
|
||||
Thread thread_sources;
|
||||
bool scanning_changes = false;
|
||||
|
|
@ -252,10 +275,12 @@ class EditorFileSystem : public Node {
|
|||
|
||||
void _update_extensions();
|
||||
|
||||
Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr);
|
||||
Error _reimport_file(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant *generator_parameters = nullptr, bool p_update_file_system = true);
|
||||
Error _reimport_group(const String &p_group_file, const Vector<String> &p_files);
|
||||
|
||||
bool _test_for_reimport(const String &p_path, bool p_only_imported_files);
|
||||
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);
|
||||
Vector<String> _get_import_dest_paths(const String &p_path);
|
||||
|
||||
bool reimport_on_missing_imported_files;
|
||||
|
||||
|
|
@ -271,20 +296,32 @@ class EditorFileSystem : public Node {
|
|||
}
|
||||
};
|
||||
|
||||
struct ScriptInfo {
|
||||
String type;
|
||||
String script_class_name;
|
||||
String script_class_extends;
|
||||
String script_class_icon_path;
|
||||
struct ScriptClassInfoUpdate : public ScriptClassInfo {
|
||||
StringName type;
|
||||
ScriptClassInfoUpdate() = default;
|
||||
explicit ScriptClassInfoUpdate(const ScriptClassInfo &p_info) :
|
||||
ScriptClassInfo(p_info) {}
|
||||
static ScriptClassInfoUpdate from_file_info(const EditorFileSystemDirectory::FileInfo *p_fi) {
|
||||
ScriptClassInfoUpdate update;
|
||||
update.type = p_fi->type;
|
||||
update.name = p_fi->class_info.name;
|
||||
update.extends = p_fi->class_info.extends;
|
||||
update.icon_path = p_fi->class_info.icon_path;
|
||||
update.is_abstract = p_fi->class_info.is_abstract;
|
||||
update.is_tool = p_fi->class_info.is_tool;
|
||||
return update;
|
||||
}
|
||||
};
|
||||
|
||||
Mutex update_script_mutex;
|
||||
HashMap<String, ScriptInfo> update_script_paths;
|
||||
HashMap<String, ScriptClassInfoUpdate> update_script_paths;
|
||||
HashSet<String> update_script_paths_documentation;
|
||||
void _queue_update_script_class(const String &p_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path);
|
||||
void _queue_update_script_class(const String &p_path, const ScriptClassInfoUpdate &p_script_update);
|
||||
void _update_script_classes();
|
||||
void _update_script_documentation();
|
||||
void _process_update_pending();
|
||||
void _process_removed_files(const HashSet<String> &p_processed_files);
|
||||
bool _should_reload_script(const String &p_path);
|
||||
|
||||
Mutex update_scene_mutex;
|
||||
HashSet<String> update_scene_paths;
|
||||
|
|
@ -293,9 +330,10 @@ class EditorFileSystem : public Node {
|
|||
void _update_pending_scene_groups();
|
||||
void _get_all_scenes(EditorFileSystemDirectory *p_dir, HashSet<String> &r_list);
|
||||
|
||||
String _get_global_script_class(const String &p_type, const String &p_path, String *r_extends, String *r_icon_path) const;
|
||||
ScriptClassInfo _get_global_script_class(const String &p_type, const String &p_path) const;
|
||||
|
||||
static Error _resource_import(const String &p_path);
|
||||
static Ref<Resource> _load_resource_on_startup(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode);
|
||||
|
||||
bool using_fat32_or_exfat; // Workaround for projects in FAT32 or exFAT filesystem (pendrives, most of the time)
|
||||
|
||||
|
|
@ -306,10 +344,23 @@ class EditorFileSystem : public Node {
|
|||
HashSet<String> group_file_cache;
|
||||
HashMap<String, String> file_icon_cache;
|
||||
|
||||
struct CopiedFile {
|
||||
String from;
|
||||
String to;
|
||||
};
|
||||
|
||||
bool refresh_queued = false;
|
||||
HashSet<ObjectID> folders_to_sort;
|
||||
|
||||
Error _copy_file(const String &p_from, const String &p_to);
|
||||
bool _copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files);
|
||||
void _queue_refresh_filesystem();
|
||||
void _refresh_filesystem();
|
||||
|
||||
struct ImportThreadData {
|
||||
const ImportFile *reimport_files;
|
||||
int reimport_from;
|
||||
SafeNumeric<int> max_index;
|
||||
Semaphore *imported_sem = nullptr;
|
||||
};
|
||||
|
||||
void _reimport_thread(uint32_t p_index, ImportThreadData *p_import_data);
|
||||
|
|
@ -323,10 +374,10 @@ class EditorFileSystem : public Node {
|
|||
|
||||
void _update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info);
|
||||
void _update_files_icon_path(EditorFileSystemDirectory *edp = nullptr);
|
||||
void _remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names);
|
||||
bool _remove_invalid_global_class_names(const HashSet<String> &p_existing_class_names);
|
||||
String _get_file_by_class_name(EditorFileSystemDirectory *p_dir, const String &p_class_name, EditorFileSystemDirectory::FileInfo *&r_file_info);
|
||||
|
||||
void _register_global_class_script(const String &p_search_path, const String &p_target_path, const String &p_type, const String &p_script_class_name, const String &p_script_class_extends, const String &p_script_class_icon_path);
|
||||
void _register_global_class_script(const String &p_search_path, const String &p_target_path, const ScriptClassInfoUpdate &p_script_update);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
|
@ -350,6 +401,7 @@ public:
|
|||
EditorFileSystemDirectory *get_filesystem_path(const String &p_path);
|
||||
String get_file_type(const String &p_file) const;
|
||||
EditorFileSystemDirectory *find_file(const String &p_file, int *r_index) const;
|
||||
ResourceUID::ID get_file_uid(const String &p_path) const;
|
||||
|
||||
void reimport_files(const Vector<String> &p_files);
|
||||
Error reimport_append(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters);
|
||||
|
|
@ -359,8 +411,14 @@ public:
|
|||
bool is_group_file(const String &p_path) const;
|
||||
void move_group_file(const String &p_path, const String &p_new_path);
|
||||
|
||||
Error make_dir_recursive(const String &p_path, const String &p_base_path = String());
|
||||
Error copy_file(const String &p_from, const String &p_to);
|
||||
Error copy_directory(const String &p_from, const String &p_to);
|
||||
|
||||
static bool _should_skip_directory(const String &p_path);
|
||||
|
||||
static void scan_for_uid();
|
||||
|
||||
void add_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
|
||||
void remove_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
|
||||
EditorFileSystem();
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p
|
|||
if (!p_node->get_owner()) {
|
||||
return; //not owned, bye
|
||||
}
|
||||
if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node)) {
|
||||
if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node->get_owner())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -249,7 +249,7 @@ void EditorFolding::_do_object_unfolds(Object *p_object, HashSet<Ref<Resource>>
|
|||
}
|
||||
}
|
||||
} else { //path
|
||||
int last = E.name.rfind("/");
|
||||
int last = E.name.rfind_char('/');
|
||||
if (last != -1) {
|
||||
bool can_revert = EditorPropertyRevert::can_property_revert(p_object, E.name);
|
||||
if (can_revert) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -32,15 +32,12 @@
|
|||
#define EDITOR_HELP_H
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "editor/code_editor.h"
|
||||
#include "editor/doc_tools.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
#include "scene/main/timer.h"
|
||||
|
||||
|
|
@ -60,6 +57,8 @@ class FindBar : public HBoxContainer {
|
|||
|
||||
int results_count = 0;
|
||||
|
||||
virtual void input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
void _hide_bar();
|
||||
|
||||
void _search_text_changed(const String &p_text);
|
||||
|
|
@ -70,7 +69,6 @@ class FindBar : public HBoxContainer {
|
|||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
bool _search(bool p_search_previous = false);
|
||||
|
||||
|
|
@ -188,7 +186,6 @@ class EditorHelp : public VBoxContainer {
|
|||
void _request_help(const String &p_string);
|
||||
void _search(bool p_search_previous = false);
|
||||
|
||||
String _fix_constant(const String &p_constant) const;
|
||||
void _toggle_scripts_pressed();
|
||||
|
||||
static int doc_generation_count;
|
||||
|
|
@ -254,6 +251,13 @@ public:
|
|||
class EditorHelpBit : public VBoxContainer {
|
||||
GDCLASS(EditorHelpBit, VBoxContainer);
|
||||
|
||||
enum SymbolHint {
|
||||
SYMBOL_HINT_NONE,
|
||||
SYMBOL_HINT_INHERITANCE, // [ < ParentClass[ < ...]]
|
||||
SYMBOL_HINT_ASSIGNABLE, // [: Type][ = value]
|
||||
SYMBOL_HINT_SIGNATURE, // (arguments)[ -> Type][ qualifiers]
|
||||
};
|
||||
|
||||
struct DocType {
|
||||
String type;
|
||||
String enumeration;
|
||||
|
|
@ -270,23 +274,32 @@ class EditorHelpBit : public VBoxContainer {
|
|||
String description;
|
||||
String deprecated_message;
|
||||
String experimental_message;
|
||||
DocType doc_type; // For method return type.
|
||||
Vector<ArgumentData> arguments; // For methods and signals.
|
||||
DocType doc_type;
|
||||
String value;
|
||||
Vector<ArgumentData> arguments;
|
||||
String qualifiers;
|
||||
String resource_path;
|
||||
};
|
||||
|
||||
inline static HashMap<StringName, HelpData> doc_class_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_enum_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_constant_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_property_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_theme_item_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_method_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_signal_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_theme_item_cache;
|
||||
inline static HashMap<StringName, HashMap<StringName, HelpData>> doc_annotation_cache;
|
||||
|
||||
RichTextLabel *title = nullptr;
|
||||
RichTextLabel *content = nullptr;
|
||||
|
||||
bool use_class_prefix = false;
|
||||
|
||||
String symbol_doc_link;
|
||||
String symbol_class_name;
|
||||
String symbol_type;
|
||||
String symbol_visible_type;
|
||||
String symbol_name;
|
||||
SymbolHint symbol_hint = SYMBOL_HINT_NONE;
|
||||
|
||||
HelpData help_data;
|
||||
|
||||
|
|
@ -294,10 +307,13 @@ class EditorHelpBit : public VBoxContainer {
|
|||
float content_max_height = 0.0;
|
||||
|
||||
static HelpData _get_class_help_data(const StringName &p_class_name);
|
||||
static HelpData _get_enum_help_data(const StringName &p_class_name, const StringName &p_enum_name);
|
||||
static HelpData _get_constant_help_data(const StringName &p_class_name, const StringName &p_constant_name);
|
||||
static HelpData _get_property_help_data(const StringName &p_class_name, const StringName &p_property_name);
|
||||
static HelpData _get_theme_item_help_data(const StringName &p_class_name, const StringName &p_theme_item_name);
|
||||
static HelpData _get_method_help_data(const StringName &p_class_name, const StringName &p_method_name);
|
||||
static HelpData _get_signal_help_data(const StringName &p_class_name, const StringName &p_signal_name);
|
||||
static HelpData _get_theme_item_help_data(const StringName &p_class_name, const StringName &p_theme_item_name);
|
||||
static HelpData _get_annotation_help_data(const StringName &p_class_name, const StringName &p_annotation_name);
|
||||
|
||||
void _add_type_to_title(const DocType &p_doc_type);
|
||||
void _update_labels();
|
||||
|
|
@ -309,15 +325,13 @@ protected:
|
|||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void parse_symbol(const String &p_symbol);
|
||||
void parse_symbol(const String &p_symbol, const String &p_prologue = String());
|
||||
void set_custom_text(const String &p_type, const String &p_name, const String &p_description);
|
||||
void set_description(const String &p_text);
|
||||
_FORCE_INLINE_ String get_description() const { return help_data.description; }
|
||||
|
||||
void set_content_height_limits(float p_min, float p_max);
|
||||
void update_content_height();
|
||||
|
||||
EditorHelpBit(const String &p_symbol = String());
|
||||
EditorHelpBit(const String &p_symbol = String(), const String &p_prologue = String(), bool p_use_class_prefix = false, bool p_allow_selection = true);
|
||||
};
|
||||
|
||||
// Standard tooltips do not allow you to hover over them.
|
||||
|
|
@ -325,20 +339,22 @@ public:
|
|||
class EditorHelpBitTooltip : public PopupPanel {
|
||||
GDCLASS(EditorHelpBitTooltip, PopupPanel);
|
||||
|
||||
static bool _is_tooltip_visible;
|
||||
|
||||
Timer *timer = nullptr;
|
||||
int _pushing_input = 0;
|
||||
bool _need_free = false;
|
||||
uint64_t _enter_tree_time = 0;
|
||||
bool _is_mouse_inside_tooltip = false;
|
||||
|
||||
static Control *_make_invisible_control();
|
||||
|
||||
void _start_timer();
|
||||
void _safe_queue_free();
|
||||
void _target_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
static void show_tooltip(EditorHelpBit *p_help_bit, Control *p_target);
|
||||
static Control *show_tooltip(Control *p_target, const String &p_symbol, const String &p_prologue = String(), bool p_use_class_prefix = false);
|
||||
|
||||
void popup_under_cursor();
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
|
||||
#include "editor_help_search.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_feature_profile.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
|
|
@ -134,34 +134,47 @@ void EditorHelpSearch::_native_action_cb(const String &p_item_string) {
|
|||
}
|
||||
|
||||
void EditorHelpSearch::_update_results() {
|
||||
String term = search_box->get_text();
|
||||
const String term = search_box->get_text().strip_edges();
|
||||
|
||||
int search_flags = filter_combo->get_selected_id();
|
||||
if (case_sensitive_button->is_pressed()) {
|
||||
search_flags |= SEARCH_CASE_SENSITIVE;
|
||||
}
|
||||
if (hierarchy_button->is_pressed()) {
|
||||
search_flags |= SEARCH_SHOW_HIERARCHY;
|
||||
}
|
||||
|
||||
search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags)));
|
||||
set_process(true);
|
||||
// Process separately if term is not short, or is "@" for annotations.
|
||||
if (term.length() > 1 || term == "@") {
|
||||
case_sensitive_button->set_disabled(false);
|
||||
hierarchy_button->set_disabled(false);
|
||||
|
||||
if (case_sensitive_button->is_pressed()) {
|
||||
search_flags |= SEARCH_CASE_SENSITIVE;
|
||||
}
|
||||
if (hierarchy_button->is_pressed()) {
|
||||
search_flags |= SEARCH_SHOW_HIERARCHY;
|
||||
}
|
||||
|
||||
search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags);
|
||||
|
||||
// Clear old search flags to force rebuild on short term.
|
||||
old_search_flags = 0;
|
||||
set_process(true);
|
||||
} else {
|
||||
// Disable hierarchy and case sensitive options, not used for short searches.
|
||||
case_sensitive_button->set_disabled(true);
|
||||
hierarchy_button->set_disabled(true);
|
||||
|
||||
// Always show hierarchy for short searches.
|
||||
search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY);
|
||||
|
||||
old_search_flags = search_flags;
|
||||
set_process(true);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorHelpSearch::_search_box_gui_input(const Ref<InputEvent> &p_event) {
|
||||
// Redirect up and down navigational key events to the results list.
|
||||
// Redirect navigational key events to the tree.
|
||||
Ref<InputEventKey> key = p_event;
|
||||
if (key.is_valid()) {
|
||||
switch (key->get_keycode()) {
|
||||
case Key::UP:
|
||||
case Key::DOWN:
|
||||
case Key::PAGEUP:
|
||||
case Key::PAGEDOWN: {
|
||||
results_tree->gui_input(key);
|
||||
search_box->accept_event();
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
if (key->is_action("ui_up", true) || key->is_action("ui_down", true) || key->is_action("ui_page_up") || key->is_action("ui_page_down")) {
|
||||
results_tree->gui_input(key);
|
||||
search_box->accept_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +194,7 @@ void EditorHelpSearch::_confirmed() {
|
|||
}
|
||||
|
||||
// Activate the script editor and emit the signal with the documentation link to display.
|
||||
EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT);
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
|
||||
|
||||
emit_signal(SNAME("go_to_help"), item->get_metadata(0));
|
||||
|
||||
|
|
@ -205,6 +218,8 @@ void EditorHelpSearch::_notification(int p_what) {
|
|||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible()) {
|
||||
tree_cache.clear();
|
||||
results_tree->get_vscroll_bar()->set_value(0);
|
||||
search = Ref<Runner>();
|
||||
callable_mp(results_tree, &Tree::clear).call_deferred(); // Wait for the Tree's mouse event propagation.
|
||||
get_ok_button()->set_disabled(true);
|
||||
EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "search_help", Rect2(get_position(), get_size()));
|
||||
|
|
@ -228,8 +243,8 @@ void EditorHelpSearch::_notification(int p_what) {
|
|||
search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
search_box->add_theme_icon_override("right_icon", get_editor_theme_icon(SNAME("Search")));
|
||||
|
||||
case_sensitive_button->set_icon(get_editor_theme_icon(SNAME("MatchCase")));
|
||||
hierarchy_button->set_icon(get_editor_theme_icon(SNAME("ClassList")));
|
||||
case_sensitive_button->set_button_icon(get_editor_theme_icon(SNAME("MatchCase")));
|
||||
hierarchy_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList")));
|
||||
|
||||
if (is_visible()) {
|
||||
_update_results();
|
||||
|
|
@ -278,6 +293,7 @@ void EditorHelpSearch::popup_dialog(const String &p_term) {
|
|||
popup_centered_ratio(0.5F);
|
||||
}
|
||||
|
||||
old_search_flags = 0;
|
||||
if (p_term.is_empty()) {
|
||||
search_box->clear();
|
||||
} else {
|
||||
|
|
@ -321,7 +337,7 @@ EditorHelpSearch::EditorHelpSearch() {
|
|||
hbox->add_child(search_box);
|
||||
|
||||
case_sensitive_button = memnew(Button);
|
||||
case_sensitive_button->set_theme_type_variation("FlatButton");
|
||||
case_sensitive_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
case_sensitive_button->set_tooltip_text(TTR("Case Sensitive"));
|
||||
case_sensitive_button->connect(SceneStringName(pressed), callable_mp(this, &EditorHelpSearch::_update_results));
|
||||
case_sensitive_button->set_toggle_mode(true);
|
||||
|
|
@ -329,7 +345,7 @@ EditorHelpSearch::EditorHelpSearch() {
|
|||
hbox->add_child(case_sensitive_button);
|
||||
|
||||
hierarchy_button = memnew(Button);
|
||||
hierarchy_button->set_theme_type_variation("FlatButton");
|
||||
hierarchy_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
hierarchy_button->set_tooltip_text(TTR("Show Hierarchy"));
|
||||
hierarchy_button->connect(SceneStringName(pressed), callable_mp(this, &EditorHelpSearch::_update_results));
|
||||
hierarchy_button->set_toggle_mode(true);
|
||||
|
|
@ -401,6 +417,237 @@ bool EditorHelpSearch::Runner::_is_class_disabled_by_feature_profile(const Strin
|
|||
return false;
|
||||
}
|
||||
|
||||
bool EditorHelpSearch::Runner::_fill() {
|
||||
bool phase_done = false;
|
||||
switch (phase) {
|
||||
case PHASE_MATCH_CLASSES_INIT:
|
||||
phase_done = _phase_fill_classes_init();
|
||||
break;
|
||||
case PHASE_MATCH_CLASSES:
|
||||
phase_done = _phase_fill_classes();
|
||||
break;
|
||||
case PHASE_CLASS_ITEMS_INIT:
|
||||
case PHASE_CLASS_ITEMS:
|
||||
phase_done = true;
|
||||
break;
|
||||
case PHASE_MEMBER_ITEMS_INIT:
|
||||
phase_done = _phase_fill_member_items_init();
|
||||
break;
|
||||
case PHASE_MEMBER_ITEMS:
|
||||
phase_done = _phase_fill_member_items();
|
||||
break;
|
||||
case PHASE_SELECT_MATCH:
|
||||
phase_done = _phase_select_match();
|
||||
break;
|
||||
case PHASE_MAX:
|
||||
return true;
|
||||
default:
|
||||
WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (phase_done) {
|
||||
phase++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorHelpSearch::Runner::_phase_fill_classes_init() {
|
||||
// Initialize fill.
|
||||
iterator_stack.clear();
|
||||
matched_classes.clear();
|
||||
matched_item = nullptr;
|
||||
match_highest_score = 0;
|
||||
|
||||
// Initialize stack of iterators to fill, in reverse.
|
||||
iterator_stack.push_back(EditorHelp::get_doc_data()->inheriting[""].back());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorHelpSearch::Runner::_phase_fill_classes() {
|
||||
if (iterator_stack.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (iterator_stack[iterator_stack.size() - 1]) {
|
||||
DocData::ClassDoc *class_doc = EditorHelp::get_doc_data()->class_list.getptr(iterator_stack[iterator_stack.size() - 1]->get());
|
||||
|
||||
// Decrement stack.
|
||||
iterator_stack[iterator_stack.size() - 1] = iterator_stack[iterator_stack.size() - 1]->prev();
|
||||
|
||||
// Drop last element of stack if empty.
|
||||
if (!iterator_stack[iterator_stack.size() - 1]) {
|
||||
iterator_stack.resize(iterator_stack.size() - 1);
|
||||
}
|
||||
|
||||
if (!class_doc || class_doc->name.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If class matches the flags, add it to the matched stack.
|
||||
const bool class_matched =
|
||||
(search_flags & SEARCH_CLASSES) ||
|
||||
((search_flags & SEARCH_CONSTRUCTORS) && !class_doc->constructors.is_empty()) ||
|
||||
((search_flags & SEARCH_METHODS) && !class_doc->methods.is_empty()) ||
|
||||
((search_flags & SEARCH_OPERATORS) && !class_doc->operators.is_empty()) ||
|
||||
((search_flags & SEARCH_SIGNALS) && !class_doc->signals.is_empty()) ||
|
||||
((search_flags & SEARCH_CONSTANTS) && !class_doc->constants.is_empty()) ||
|
||||
((search_flags & SEARCH_PROPERTIES) && !class_doc->properties.is_empty()) ||
|
||||
((search_flags & SEARCH_THEME_ITEMS) && !class_doc->theme_properties.is_empty()) ||
|
||||
((search_flags & SEARCH_ANNOTATIONS) && !class_doc->annotations.is_empty());
|
||||
|
||||
if (class_matched) {
|
||||
if (term.is_empty() || class_doc->name.containsn(term)) {
|
||||
matched_classes.push_back(Pair<DocData::ClassDoc *, String>(class_doc, String()));
|
||||
} else if (String keyword = _match_keywords(term, class_doc->keywords); !keyword.is_empty()) {
|
||||
matched_classes.push_back(Pair<DocData::ClassDoc *, String>(class_doc, keyword));
|
||||
}
|
||||
}
|
||||
|
||||
// Add inheriting classes, in reverse.
|
||||
if (class_doc && EditorHelp::get_doc_data()->inheriting.has(class_doc->name)) {
|
||||
iterator_stack.push_back(EditorHelp::get_doc_data()->inheriting[class_doc->name].back());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop last element of stack if empty.
|
||||
if (!iterator_stack[iterator_stack.size() - 1]) {
|
||||
iterator_stack.resize(iterator_stack.size() - 1);
|
||||
}
|
||||
|
||||
return iterator_stack.is_empty();
|
||||
}
|
||||
|
||||
bool EditorHelpSearch::Runner::_phase_fill_member_items_init() {
|
||||
// Prepare tree.
|
||||
class_items.clear();
|
||||
_populate_cache();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_category_item(TreeItem *p_parent, const String &p_class, const StringName &p_icon, const String &p_text, const String &p_metatype) {
|
||||
const String item_meta = "class_" + p_metatype + ":" + p_class;
|
||||
|
||||
TreeItem *item = nullptr;
|
||||
if (_find_or_create_item(p_parent, item_meta, item)) {
|
||||
item->set_icon(0, ui_service->get_editor_theme_icon(p_icon));
|
||||
item->set_text(0, p_text);
|
||||
item->set_metadata(0, item_meta);
|
||||
}
|
||||
item->set_collapsed(true);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
bool EditorHelpSearch::Runner::_phase_fill_member_items() {
|
||||
if (matched_classes.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pop working item from stack.
|
||||
Pair<DocData::ClassDoc *, String> match = matched_classes[matched_classes.size() - 1];
|
||||
DocData::ClassDoc *class_doc = match.first;
|
||||
const String &keyword = match.second;
|
||||
matched_classes.resize(matched_classes.size() - 1);
|
||||
|
||||
if (class_doc) {
|
||||
TreeItem *item = _create_class_hierarchy(class_doc, keyword, !(search_flags & SEARCH_CLASSES));
|
||||
|
||||
// If the class has no inheriting classes, fold its item.
|
||||
item->set_collapsed(!item->get_first_child());
|
||||
|
||||
if (search_flags & SEARCH_CLASSES) {
|
||||
item->clear_custom_color(0);
|
||||
item->clear_custom_color(1);
|
||||
} else {
|
||||
item->set_custom_color(0, disabled_color);
|
||||
item->set_custom_color(1, disabled_color);
|
||||
}
|
||||
|
||||
// Create common header if required.
|
||||
const bool search_all = (search_flags & SEARCH_ALL) == SEARCH_ALL;
|
||||
|
||||
if ((search_flags & SEARCH_CONSTRUCTORS) && !class_doc->constructors.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberConstructor"), TTRC("Constructors"), "constructors");
|
||||
}
|
||||
for (const DocData::MethodDoc &constructor_doc : class_doc->constructors) {
|
||||
_create_constructor_item(parent_item, class_doc, &constructor_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_METHODS) && !class_doc->methods.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberMethod"), TTRC("Methods"), "methods");
|
||||
}
|
||||
for (const DocData::MethodDoc &method_doc : class_doc->methods) {
|
||||
_create_method_item(parent_item, class_doc, &method_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_OPERATORS) && !class_doc->operators.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberOperator"), TTRC("Operators"), "operators");
|
||||
}
|
||||
for (const DocData::MethodDoc &operator_doc : class_doc->operators) {
|
||||
_create_operator_item(parent_item, class_doc, &operator_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_SIGNALS) && !class_doc->signals.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberSignal"), TTRC("Signals"), "signals");
|
||||
}
|
||||
for (const DocData::MethodDoc &signal_doc : class_doc->signals) {
|
||||
_create_signal_item(parent_item, class_doc, &signal_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_CONSTANTS) && !class_doc->constants.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberConstant"), TTRC("Constants"), "constants");
|
||||
}
|
||||
for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
|
||||
_create_constant_item(parent_item, class_doc, &constant_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_PROPERTIES) && !class_doc->properties.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberProperty"), TTRC("Properties"), "properties");
|
||||
}
|
||||
for (const DocData::PropertyDoc &property_doc : class_doc->properties) {
|
||||
_create_property_item(parent_item, class_doc, &property_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_THEME_ITEMS) && !class_doc->theme_properties.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberTheme"), TTRC("Theme Properties"), "theme_items");
|
||||
}
|
||||
for (const DocData::ThemeItemDoc &theme_property_doc : class_doc->theme_properties) {
|
||||
_create_theme_property_item(parent_item, class_doc, &theme_property_doc);
|
||||
}
|
||||
}
|
||||
if ((search_flags & SEARCH_ANNOTATIONS) && !class_doc->annotations.is_empty()) {
|
||||
TreeItem *parent_item = item;
|
||||
if (search_all) {
|
||||
parent_item = _create_category_item(parent_item, class_doc->name, SNAME("MemberAnnotation"), TTRC("Annotations"), "annotations");
|
||||
}
|
||||
for (const DocData::MethodDoc &annotation_doc : class_doc->annotations) {
|
||||
_create_annotation_item(parent_item, class_doc, &annotation_doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matched_classes.is_empty();
|
||||
}
|
||||
|
||||
bool EditorHelpSearch::Runner::_slice() {
|
||||
bool phase_done = false;
|
||||
switch (phase) {
|
||||
|
|
@ -430,7 +677,7 @@ bool EditorHelpSearch::Runner::_slice() {
|
|||
default:
|
||||
WARN_PRINT("Invalid or unhandled phase in EditorHelpSearch::Runner, aborting search.");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
if (phase_done) {
|
||||
phase++;
|
||||
|
|
@ -450,9 +697,11 @@ bool EditorHelpSearch::Runner::_phase_match_classes_init() {
|
|||
matched_item = nullptr;
|
||||
match_highest_score = 0;
|
||||
|
||||
terms = term.split_spaces();
|
||||
if (terms.is_empty()) {
|
||||
terms.append(term);
|
||||
if (!term.is_empty()) {
|
||||
terms = term.split_spaces();
|
||||
if (terms.is_empty()) {
|
||||
terms.append(term);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -480,78 +729,71 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
|
|||
|
||||
// Match class name.
|
||||
if (search_flags & SEARCH_CLASSES) {
|
||||
// If the search term is empty, add any classes which are not script docs or which don't start with
|
||||
// a double-quotation. This will ensure that only C++ classes and explicitly named classes will
|
||||
// be added.
|
||||
match.name = (term.is_empty() && (!class_doc->is_script_doc || class_doc->name[0] != '\"')) || _match_string(term, class_doc->name);
|
||||
match.name = _match_string(term, class_doc->name);
|
||||
match.keyword = _match_keywords(term, class_doc->keywords);
|
||||
}
|
||||
|
||||
// Match members only if the term is long enough, to avoid slow performance from building a large tree.
|
||||
// Make an exception for annotations, since there are not that many of them.
|
||||
if (term.length() > 1 || term == "@") {
|
||||
if (search_flags & SEARCH_CONSTRUCTORS) {
|
||||
_match_method_name_and_push_back(class_doc->constructors, &match.constructors);
|
||||
}
|
||||
if (search_flags & SEARCH_METHODS) {
|
||||
_match_method_name_and_push_back(class_doc->methods, &match.methods);
|
||||
}
|
||||
if (search_flags & SEARCH_OPERATORS) {
|
||||
_match_method_name_and_push_back(class_doc->operators, &match.operators);
|
||||
}
|
||||
if (search_flags & SEARCH_SIGNALS) {
|
||||
for (int i = 0; i < class_doc->signals.size(); i++) {
|
||||
MemberMatch<DocData::MethodDoc> signal;
|
||||
signal.name = _all_terms_in_name(class_doc->signals[i].name);
|
||||
signal.keyword = _match_keywords_in_all_terms(class_doc->signals[i].keywords);
|
||||
if (signal.name || !signal.keyword.is_empty()) {
|
||||
signal.doc = const_cast<DocData::MethodDoc *>(&class_doc->signals[i]);
|
||||
match.signals.push_back(signal);
|
||||
}
|
||||
if (search_flags & SEARCH_CONSTRUCTORS) {
|
||||
_match_method_name_and_push_back(class_doc->constructors, &match.constructors);
|
||||
}
|
||||
if (search_flags & SEARCH_METHODS) {
|
||||
_match_method_name_and_push_back(class_doc->methods, &match.methods);
|
||||
}
|
||||
if (search_flags & SEARCH_OPERATORS) {
|
||||
_match_method_name_and_push_back(class_doc->operators, &match.operators);
|
||||
}
|
||||
if (search_flags & SEARCH_SIGNALS) {
|
||||
for (const DocData::MethodDoc &signal_doc : class_doc->signals) {
|
||||
MemberMatch<DocData::MethodDoc> signal;
|
||||
signal.name = _all_terms_in_name(signal_doc.name);
|
||||
signal.keyword = _match_keywords_in_all_terms(signal_doc.keywords);
|
||||
if (signal.name || !signal.keyword.is_empty()) {
|
||||
signal.doc = &signal_doc;
|
||||
match.signals.push_back(signal);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_CONSTANTS) {
|
||||
for (int i = 0; i < class_doc->constants.size(); i++) {
|
||||
MemberMatch<DocData::ConstantDoc> constant;
|
||||
constant.name = _all_terms_in_name(class_doc->constants[i].name);
|
||||
constant.keyword = _match_keywords_in_all_terms(class_doc->constants[i].keywords);
|
||||
if (constant.name || !constant.keyword.is_empty()) {
|
||||
constant.doc = const_cast<DocData::ConstantDoc *>(&class_doc->constants[i]);
|
||||
match.constants.push_back(constant);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_CONSTANTS) {
|
||||
for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
|
||||
MemberMatch<DocData::ConstantDoc> constant;
|
||||
constant.name = _all_terms_in_name(constant_doc.name);
|
||||
constant.keyword = _match_keywords_in_all_terms(constant_doc.keywords);
|
||||
if (constant.name || !constant.keyword.is_empty()) {
|
||||
constant.doc = &constant_doc;
|
||||
match.constants.push_back(constant);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_PROPERTIES) {
|
||||
for (int i = 0; i < class_doc->properties.size(); i++) {
|
||||
MemberMatch<DocData::PropertyDoc> property;
|
||||
property.name = _all_terms_in_name(class_doc->properties[i].name);
|
||||
property.keyword = _match_keywords_in_all_terms(class_doc->properties[i].keywords);
|
||||
if (property.name || !property.keyword.is_empty()) {
|
||||
property.doc = const_cast<DocData::PropertyDoc *>(&class_doc->properties[i]);
|
||||
match.properties.push_back(property);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_PROPERTIES) {
|
||||
for (const DocData::PropertyDoc &property_doc : class_doc->properties) {
|
||||
MemberMatch<DocData::PropertyDoc> property;
|
||||
property.name = _all_terms_in_name(property_doc.name);
|
||||
property.keyword = _match_keywords_in_all_terms(property_doc.keywords);
|
||||
if (property.name || !property.keyword.is_empty()) {
|
||||
property.doc = &property_doc;
|
||||
match.properties.push_back(property);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_THEME_ITEMS) {
|
||||
for (int i = 0; i < class_doc->theme_properties.size(); i++) {
|
||||
MemberMatch<DocData::ThemeItemDoc> theme_property;
|
||||
theme_property.name = _all_terms_in_name(class_doc->theme_properties[i].name);
|
||||
theme_property.keyword = _match_keywords_in_all_terms(class_doc->theme_properties[i].keywords);
|
||||
if (theme_property.name || !theme_property.keyword.is_empty()) {
|
||||
theme_property.doc = const_cast<DocData::ThemeItemDoc *>(&class_doc->theme_properties[i]);
|
||||
match.theme_properties.push_back(theme_property);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_THEME_ITEMS) {
|
||||
for (const DocData::ThemeItemDoc &theme_property_doc : class_doc->theme_properties) {
|
||||
MemberMatch<DocData::ThemeItemDoc> theme_property;
|
||||
theme_property.name = _all_terms_in_name(theme_property_doc.name);
|
||||
theme_property.keyword = _match_keywords_in_all_terms(theme_property_doc.keywords);
|
||||
if (theme_property.name || !theme_property.keyword.is_empty()) {
|
||||
theme_property.doc = &theme_property_doc;
|
||||
match.theme_properties.push_back(theme_property);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_ANNOTATIONS) {
|
||||
for (int i = 0; i < class_doc->annotations.size(); i++) {
|
||||
MemberMatch<DocData::MethodDoc> annotation;
|
||||
annotation.name = _all_terms_in_name(class_doc->annotations[i].name);
|
||||
annotation.keyword = _match_keywords_in_all_terms(class_doc->annotations[i].keywords);
|
||||
if (annotation.name || !annotation.keyword.is_empty()) {
|
||||
annotation.doc = const_cast<DocData::MethodDoc *>(&class_doc->annotations[i]);
|
||||
match.annotations.push_back(annotation);
|
||||
}
|
||||
}
|
||||
if (search_flags & SEARCH_ANNOTATIONS) {
|
||||
for (const DocData::MethodDoc &annotation_doc : class_doc->annotations) {
|
||||
MemberMatch<DocData::MethodDoc> annotation;
|
||||
annotation.name = _all_terms_in_name(annotation_doc.name);
|
||||
annotation.keyword = _match_keywords_in_all_terms(annotation_doc.keywords);
|
||||
if (annotation.name || !annotation.keyword.is_empty()) {
|
||||
annotation.doc = &annotation_doc;
|
||||
match.annotations.push_back(annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -564,9 +806,11 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
|
|||
}
|
||||
|
||||
if (!iterator_stack.is_empty()) {
|
||||
// Iterate on stack.
|
||||
if (iterator_stack[iterator_stack.size() - 1]) {
|
||||
iterator_stack[iterator_stack.size() - 1] = iterator_stack[iterator_stack.size() - 1]->next();
|
||||
}
|
||||
// Drop last element of stack.
|
||||
if (!iterator_stack[iterator_stack.size() - 1]) {
|
||||
iterator_stack.resize(iterator_stack.size() - 1);
|
||||
}
|
||||
|
|
@ -661,36 +905,32 @@ bool EditorHelpSearch::Runner::_phase_member_items() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Pick appropriate parent item if showing hierarchy, otherwise pick root.
|
||||
TreeItem *parent_item = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
|
||||
bool constructor_created = false;
|
||||
for (int i = 0; i < match.methods.size(); i++) {
|
||||
String text = match.methods[i].doc->name;
|
||||
if (!constructor_created) {
|
||||
if (match.doc->name == match.methods[i].doc->name) {
|
||||
text += " " + TTR("(constructors)");
|
||||
constructor_created = true;
|
||||
}
|
||||
} else {
|
||||
if (match.doc->name == match.methods[i].doc->name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_create_method_item(parent_item, match.doc, text, match.methods[i]);
|
||||
|
||||
for (const MemberMatch<DocData::MethodDoc> &constructor_item : match.constructors) {
|
||||
_create_constructor_item(parent_item, match.doc, constructor_item);
|
||||
}
|
||||
for (int i = 0; i < match.signals.size(); i++) {
|
||||
_create_signal_item(parent_item, match.doc, match.signals[i]);
|
||||
for (const MemberMatch<DocData::MethodDoc> &method_item : match.methods) {
|
||||
_create_method_item(parent_item, match.doc, method_item);
|
||||
}
|
||||
for (int i = 0; i < match.constants.size(); i++) {
|
||||
_create_constant_item(parent_item, match.doc, match.constants[i]);
|
||||
for (const MemberMatch<DocData::MethodDoc> &operator_item : match.operators) {
|
||||
_create_operator_item(parent_item, match.doc, operator_item);
|
||||
}
|
||||
for (int i = 0; i < match.properties.size(); i++) {
|
||||
_create_property_item(parent_item, match.doc, match.properties[i]);
|
||||
for (const MemberMatch<DocData::MethodDoc> &signal_item : match.signals) {
|
||||
_create_signal_item(parent_item, match.doc, signal_item);
|
||||
}
|
||||
for (int i = 0; i < match.theme_properties.size(); i++) {
|
||||
_create_theme_property_item(parent_item, match.doc, match.theme_properties[i]);
|
||||
for (const MemberMatch<DocData::ConstantDoc> &constant_item : match.constants) {
|
||||
_create_constant_item(parent_item, match.doc, constant_item);
|
||||
}
|
||||
for (int i = 0; i < match.annotations.size(); i++) {
|
||||
_create_annotation_item(parent_item, match.doc, match.annotations[i]);
|
||||
for (const MemberMatch<DocData::PropertyDoc> &property_item : match.properties) {
|
||||
_create_property_item(parent_item, match.doc, property_item);
|
||||
}
|
||||
for (const MemberMatch<DocData::ThemeItemDoc> &theme_property_item : match.theme_properties) {
|
||||
_create_theme_property_item(parent_item, match.doc, theme_property_item);
|
||||
}
|
||||
for (const MemberMatch<DocData::MethodDoc> &annotation_item : match.annotations) {
|
||||
_create_annotation_item(parent_item, match.doc, annotation_item);
|
||||
}
|
||||
|
||||
++iterator_match;
|
||||
|
|
@ -704,7 +944,7 @@ bool EditorHelpSearch::Runner::_phase_select_match() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<MemberMatch<DocData::MethodDoc>> *r_match_methods) {
|
||||
void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, LocalVector<MemberMatch<DocData::MethodDoc>> *r_match_methods) {
|
||||
// Constructors, Methods, Operators...
|
||||
for (int i = 0; i < p_methods.size(); i++) {
|
||||
String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? p_methods[i].name : p_methods[i].name.to_lower();
|
||||
|
|
@ -716,7 +956,7 @@ void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::
|
|||
(term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
|
||||
(term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
|
||||
(term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
|
||||
method.doc = const_cast<DocData::MethodDoc *>(&p_methods[i]);
|
||||
method.doc = &p_methods[i];
|
||||
r_match_methods->push_back(method);
|
||||
}
|
||||
}
|
||||
|
|
@ -765,12 +1005,12 @@ void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_tex
|
|||
return;
|
||||
}
|
||||
|
||||
float inverse_length = 1.f / float(p_text.length());
|
||||
float inverse_length = 1.0f / float(p_text.length());
|
||||
|
||||
// Favor types where search term is a substring close to the start of the type.
|
||||
float w = 0.5f;
|
||||
int pos = p_text.findn(term);
|
||||
float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.f, .9f - w);
|
||||
float score = (pos > -1) ? 1.0f - w * MIN(1, 3 * pos * inverse_length) : MAX(0.0f, 0.9f - w);
|
||||
|
||||
// Favor shorter items: they resemble the search term more.
|
||||
w = 0.1f;
|
||||
|
|
@ -781,7 +1021,8 @@ void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_tex
|
|||
score *= 0.9f;
|
||||
}
|
||||
|
||||
if (match_highest_score == 0 || score > match_highest_score) {
|
||||
// Replace current match if term is short as we are searching in reverse.
|
||||
if (match_highest_score == 0 || score > match_highest_score || (score == match_highest_score && term.length() == 1)) {
|
||||
matched_item = p_item;
|
||||
match_highest_score = score;
|
||||
}
|
||||
|
|
@ -820,6 +1061,29 @@ String EditorHelpSearch::Runner::_build_keywords_tooltip(const String &p_keyword
|
|||
return tooltip.left(-2);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const DocData::ClassDoc *p_class_doc, const String &p_matching_keyword, bool p_gray) {
|
||||
if (p_class_doc->name.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (TreeItem **found = class_items.getptr(p_class_doc->name)) {
|
||||
return *found;
|
||||
}
|
||||
|
||||
// Ensure parent nodes are created first.
|
||||
TreeItem *parent_item = root_item;
|
||||
if (!p_class_doc->inherits.is_empty()) {
|
||||
if (class_items.has(p_class_doc->inherits)) {
|
||||
parent_item = class_items[p_class_doc->inherits];
|
||||
} else if (const DocData::ClassDoc *found = EditorHelp::get_doc_data()->class_list.getptr(p_class_doc->inherits)) {
|
||||
parent_item = _create_class_hierarchy(found, String(), true);
|
||||
}
|
||||
}
|
||||
|
||||
TreeItem *class_item = _create_class_item(parent_item, p_class_doc, p_gray, p_matching_keyword);
|
||||
class_items[p_class_doc->name] = class_item;
|
||||
return class_item;
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_match) {
|
||||
if (p_match.doc->name.is_empty()) {
|
||||
return nullptr;
|
||||
|
|
@ -887,6 +1151,8 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
|
|||
item->add_button(0, warning_icon, 0, false, TTR("This class is marked as experimental."));
|
||||
}
|
||||
}
|
||||
// Cached item might be collapsed.
|
||||
item->set_collapsed(false);
|
||||
|
||||
if (p_gray) {
|
||||
item->set_custom_color(0, disabled_color);
|
||||
|
|
@ -899,10 +1165,12 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
|
|||
if (p_matching_keyword.is_empty()) {
|
||||
item->set_text(0, p_doc->name);
|
||||
} else {
|
||||
item->set_text(0, p_doc->name + " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword)));
|
||||
item->set_text(0, p_doc->name + " - " + vformat(TTR("Matches the \"%s\" keyword."), p_matching_keyword));
|
||||
}
|
||||
|
||||
_match_item(item, p_doc->name);
|
||||
if (!term.is_empty()) {
|
||||
_match_item(item, p_doc->name);
|
||||
}
|
||||
for (const String &keyword : p_doc->keywords.split(",")) {
|
||||
_match_item(item, keyword.strip_edges(), true);
|
||||
}
|
||||
|
|
@ -910,44 +1178,73 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
|
|||
return item;
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const MemberMatch<DocData::MethodDoc> &p_match) {
|
||||
TreeItem *EditorHelpSearch::Runner::_create_constructor_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
|
||||
String tooltip = p_class_doc->name + "(";
|
||||
String text = p_class_doc->name + "(";
|
||||
for (int i = 0; i < p_match.doc->arguments.size(); i++) {
|
||||
const DocData::ArgumentDoc &arg = p_match.doc->arguments[i];
|
||||
tooltip += arg.type + " " + arg.name;
|
||||
text += arg.type;
|
||||
if (!arg.default_value.is_empty()) {
|
||||
tooltip += " = " + arg.default_value;
|
||||
}
|
||||
if (i < p_match.doc->arguments.size() - 1) {
|
||||
tooltip += ", ";
|
||||
text += ", ";
|
||||
}
|
||||
}
|
||||
tooltip += ")";
|
||||
tooltip += _build_keywords_tooltip(p_match.doc->keywords);
|
||||
text += ")";
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberConstructor"), p_match.doc->name, text, TTRC("Constructor"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
|
||||
String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
|
||||
return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_match.doc->name, p_text, TTRC("Method"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberMethod"), p_match.doc->name, p_match.doc->name, TTRC("Method"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_operator_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
|
||||
String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
|
||||
String text = p_match.doc->name;
|
||||
if (!p_match.doc->arguments.is_empty()) {
|
||||
text += "(" + p_match.doc->arguments[0].type + ")";
|
||||
}
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberOperator"), p_match.doc->name, text, TTRC("Operator"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
|
||||
String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
|
||||
return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_match.doc->name, p_match.doc->name, TTRC("Signal"), "signal", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberSignal"), p_match.doc->name, p_match.doc->name, TTRC("Signal"), "signal", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
|
||||
String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
|
||||
// Hide the redundant leading @ symbol.
|
||||
String text = p_match.doc->name.substr(1);
|
||||
return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_match.doc->name, text, TTRC("Annotation"), "annotation", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberAnnotation"), p_match.doc->name, text, TTRC("Annotation"), "annotation", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ConstantDoc> &p_match) {
|
||||
String tooltip = p_class_doc->name + "." + p_match.doc->name;
|
||||
tooltip += _build_keywords_tooltip(p_match.doc->keywords);
|
||||
return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_match.doc->name, p_match.doc->name, TTRC("Constant"), "constant", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberConstant"), p_match.doc->name, p_match.doc->name, TTRC("Constant"), "constant", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::PropertyDoc> &p_match) {
|
||||
String tooltip = p_match.doc->type + " " + p_class_doc->name + "." + p_match.doc->name;
|
||||
tooltip += "\n " + p_class_doc->name + "." + p_match.doc->setter + "(value) setter";
|
||||
tooltip += "\n " + p_class_doc->name + "." + p_match.doc->getter + "() getter";
|
||||
tooltip += _build_keywords_tooltip(p_match.doc->keywords);
|
||||
return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_match.doc->name, p_match.doc->name, TTRC("Property"), "property", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberProperty"), p_match.doc->name, p_match.doc->name, TTRC("Property"), "property", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ThemeItemDoc> &p_match) {
|
||||
String tooltip = p_match.doc->type + " " + p_class_doc->name + "." + p_match.doc->name;
|
||||
tooltip += _build_keywords_tooltip(p_match.doc->keywords);
|
||||
return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_match.doc->name, p_match.doc->name, TTRC("Theme Property"), "theme_item", p_match.doc->keywords, tooltip, false, false, p_match.name ? String() : p_match.keyword);
|
||||
return _create_member_item(p_parent, p_class_doc->name, SNAME("MemberTheme"), p_match.doc->name, p_match.doc->name, TTRC("Theme Property"), "theme_item", p_match.doc->keywords, tooltip, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
|
||||
}
|
||||
|
||||
TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword) {
|
||||
TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const StringName &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword) {
|
||||
const String item_meta = "class_" + p_metatype + ":" + p_class_name + ":" + p_name;
|
||||
|
||||
TreeItem *item = nullptr;
|
||||
|
|
@ -974,11 +1271,14 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons
|
|||
text = p_class_name + "." + p_text;
|
||||
}
|
||||
if (!p_matching_keyword.is_empty()) {
|
||||
text += " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword));
|
||||
text += " - " + vformat(TTR("Matches the \"%s\" keyword."), p_matching_keyword);
|
||||
}
|
||||
item->set_text(0, text);
|
||||
|
||||
_match_item(item, p_name);
|
||||
// Don't match member items for short searches.
|
||||
if (term.length() > 1 || term == "@") {
|
||||
_match_item(item, p_name);
|
||||
}
|
||||
for (const String &keyword : p_keywords.split(",")) {
|
||||
_match_item(item, keyword.strip_edges(), true);
|
||||
}
|
||||
|
|
@ -989,9 +1289,17 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons
|
|||
bool EditorHelpSearch::Runner::work(uint64_t slot) {
|
||||
// Return true when the search has been completed, otherwise false.
|
||||
const uint64_t until = OS::get_singleton()->get_ticks_usec() + slot;
|
||||
while (!_slice()) {
|
||||
if (OS::get_singleton()->get_ticks_usec() > until) {
|
||||
return false;
|
||||
if (term.length() > 1 || term == "@") {
|
||||
while (!_slice()) {
|
||||
if (OS::get_singleton()->get_ticks_usec() > until) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (!_fill()) {
|
||||
if (OS::get_singleton()->get_ticks_usec() > until) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -1001,7 +1309,7 @@ EditorHelpSearch::Runner::Runner(Control *p_icon_service, Tree *p_results_tree,
|
|||
ui_service(p_icon_service),
|
||||
results_tree(p_results_tree),
|
||||
tree_cache(p_tree_cache),
|
||||
term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.strip_edges().to_lower() : p_term.strip_edges()),
|
||||
term((p_search_flags & SEARCH_CASE_SENSITIVE) == 0 ? p_term.to_lower() : p_term),
|
||||
search_flags(p_search_flags),
|
||||
disabled_color(ui_service->get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,8 @@
|
|||
#ifndef EDITOR_HELP_SEARCH_H
|
||||
#define EDITOR_HELP_SEARCH_H
|
||||
|
||||
#include "core/templates/rb_map.h"
|
||||
#include "editor/code_editor.h"
|
||||
#include "editor/editor_help.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/tree.h"
|
||||
|
||||
|
|
@ -63,6 +61,7 @@ class EditorHelpSearch : public ConfirmationDialog {
|
|||
Tree *results_tree = nullptr;
|
||||
bool old_search = false;
|
||||
String old_term;
|
||||
int old_search_flags = 0;
|
||||
|
||||
class Runner;
|
||||
Ref<Runner> search;
|
||||
|
|
@ -119,26 +118,30 @@ class EditorHelpSearch::Runner : public RefCounted {
|
|||
|
||||
template <typename T>
|
||||
struct MemberMatch {
|
||||
T *doc = nullptr;
|
||||
const T *doc = nullptr;
|
||||
bool name = false;
|
||||
String keyword;
|
||||
|
||||
MemberMatch() {}
|
||||
MemberMatch(const T *p_doc) :
|
||||
doc(p_doc) {}
|
||||
};
|
||||
|
||||
struct ClassMatch {
|
||||
DocData::ClassDoc *doc = nullptr;
|
||||
const DocData::ClassDoc *doc = nullptr;
|
||||
bool name = false;
|
||||
String keyword;
|
||||
Vector<MemberMatch<DocData::MethodDoc>> constructors;
|
||||
Vector<MemberMatch<DocData::MethodDoc>> methods;
|
||||
Vector<MemberMatch<DocData::MethodDoc>> operators;
|
||||
Vector<MemberMatch<DocData::MethodDoc>> signals;
|
||||
Vector<MemberMatch<DocData::ConstantDoc>> constants;
|
||||
Vector<MemberMatch<DocData::PropertyDoc>> properties;
|
||||
Vector<MemberMatch<DocData::ThemeItemDoc>> theme_properties;
|
||||
Vector<MemberMatch<DocData::MethodDoc>> annotations;
|
||||
LocalVector<MemberMatch<DocData::MethodDoc>> constructors;
|
||||
LocalVector<MemberMatch<DocData::MethodDoc>> methods;
|
||||
LocalVector<MemberMatch<DocData::MethodDoc>> operators;
|
||||
LocalVector<MemberMatch<DocData::MethodDoc>> signals;
|
||||
LocalVector<MemberMatch<DocData::ConstantDoc>> constants;
|
||||
LocalVector<MemberMatch<DocData::PropertyDoc>> properties;
|
||||
LocalVector<MemberMatch<DocData::ThemeItemDoc>> theme_properties;
|
||||
LocalVector<MemberMatch<DocData::MethodDoc>> annotations;
|
||||
|
||||
bool required() {
|
||||
return name || !keyword.is_empty() || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size() || annotations.size();
|
||||
return name || !keyword.is_empty() || !constructors.is_empty() || !methods.is_empty() || !operators.is_empty() || !signals.is_empty() || !constants.is_empty() || !properties.is_empty() || !theme_properties.is_empty() || !annotations.is_empty();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -155,6 +158,7 @@ class EditorHelpSearch::Runner : public RefCounted {
|
|||
LocalVector<RBSet<String, NaturalNoCaseComparator>::Element *> iterator_stack;
|
||||
HashMap<String, ClassMatch> matches;
|
||||
HashMap<String, ClassMatch>::Iterator iterator_match;
|
||||
LocalVector<Pair<DocData::ClassDoc *, String>> matched_classes;
|
||||
TreeItem *root_item = nullptr;
|
||||
HashMap<String, TreeItem *> class_items;
|
||||
TreeItem *matched_item = nullptr;
|
||||
|
|
@ -165,6 +169,12 @@ class EditorHelpSearch::Runner : public RefCounted {
|
|||
void _populate_cache();
|
||||
bool _find_or_create_item(TreeItem *p_parent, const String &p_item_meta, TreeItem *&r_item);
|
||||
|
||||
bool _fill();
|
||||
bool _phase_fill_classes_init();
|
||||
bool _phase_fill_classes();
|
||||
bool _phase_fill_member_items_init();
|
||||
bool _phase_fill_member_items();
|
||||
|
||||
bool _slice();
|
||||
bool _phase_match_classes_init();
|
||||
bool _phase_match_classes();
|
||||
|
|
@ -177,21 +187,25 @@ class EditorHelpSearch::Runner : public RefCounted {
|
|||
String _build_method_tooltip(const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) const;
|
||||
String _build_keywords_tooltip(const String &p_keywords) const;
|
||||
|
||||
void _match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<MemberMatch<DocData::MethodDoc>> *r_match_methods);
|
||||
void _match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, LocalVector<MemberMatch<DocData::MethodDoc>> *r_match_methods);
|
||||
bool _all_terms_in_name(const String &p_name) const;
|
||||
String _match_keywords_in_all_terms(const String &p_keywords) const;
|
||||
bool _match_string(const String &p_term, const String &p_string) const;
|
||||
String _match_keywords(const String &p_term, const String &p_keywords) const;
|
||||
void _match_item(TreeItem *p_item, const String &p_text, bool p_is_keywords = false);
|
||||
TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
|
||||
TreeItem *_create_class_hierarchy(const DocData::ClassDoc *p_class_doc, const String &p_matching_keyword, bool p_gray);
|
||||
TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray, const String &p_matching_keyword);
|
||||
TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const MemberMatch<DocData::MethodDoc> &p_match);
|
||||
TreeItem *_create_category_item(TreeItem *p_parent, const String &p_class, const StringName &p_icon, const String &p_text, const String &p_metatype);
|
||||
TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
|
||||
TreeItem *_create_constructor_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
|
||||
TreeItem *_create_operator_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
|
||||
TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
|
||||
TreeItem *_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
|
||||
TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ConstantDoc> &p_match);
|
||||
TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::PropertyDoc> &p_match);
|
||||
TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ThemeItemDoc> &p_match);
|
||||
TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword);
|
||||
TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const StringName &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword);
|
||||
|
||||
public:
|
||||
bool work(uint64_t slot = 100000);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -31,6 +31,7 @@
|
|||
#ifndef EDITOR_INSPECTOR_H
|
||||
#define EDITOR_INSPECTOR_H
|
||||
|
||||
#include "editor/add_metadata_dialog.h"
|
||||
#include "editor_property_name_processor.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
|
|
@ -40,6 +41,7 @@ class Button;
|
|||
class ConfirmationDialog;
|
||||
class EditorInspector;
|
||||
class EditorValidationPanel;
|
||||
class HSeparator;
|
||||
class LineEdit;
|
||||
class MarginContainer;
|
||||
class OptionButton;
|
||||
|
|
@ -63,6 +65,7 @@ public:
|
|||
MENU_COPY_VALUE,
|
||||
MENU_PASTE_VALUE,
|
||||
MENU_COPY_PROPERTY_PATH,
|
||||
MENU_FAVORITE_PROPERTY,
|
||||
MENU_PIN_VALUE,
|
||||
MENU_OPEN_DOCUMENTATION,
|
||||
};
|
||||
|
|
@ -86,6 +89,8 @@ private:
|
|||
|
||||
int property_usage;
|
||||
|
||||
bool draw_label = true;
|
||||
bool draw_background = true;
|
||||
bool read_only = false;
|
||||
bool checkable = false;
|
||||
bool checked = false;
|
||||
|
|
@ -111,6 +116,9 @@ private:
|
|||
bool pin_hidden = false;
|
||||
bool pinned = false;
|
||||
|
||||
bool can_favorite = false;
|
||||
bool favorited = false;
|
||||
|
||||
bool use_folding = false;
|
||||
bool draw_top_bg = true;
|
||||
|
||||
|
|
@ -133,7 +141,7 @@ private:
|
|||
GDVIRTUAL0(_update_property)
|
||||
GDVIRTUAL1(_set_read_only, bool)
|
||||
|
||||
void _update_pin_flags();
|
||||
void _update_flags();
|
||||
|
||||
protected:
|
||||
bool has_borders = false;
|
||||
|
|
@ -154,6 +162,8 @@ protected:
|
|||
public:
|
||||
void emit_changed(const StringName &p_property, const Variant &p_value, const StringName &p_field = StringName(), bool p_changing = false);
|
||||
|
||||
String get_tooltip_string(const String &p_string) const;
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
void set_label(const String &p_label);
|
||||
|
|
@ -162,9 +172,18 @@ public:
|
|||
void set_read_only(bool p_read_only);
|
||||
bool is_read_only() const;
|
||||
|
||||
void set_draw_label(bool p_draw_label);
|
||||
bool is_draw_label() const;
|
||||
|
||||
void set_draw_background(bool p_draw_background);
|
||||
bool is_draw_background() const;
|
||||
|
||||
Object *get_edited_object();
|
||||
StringName get_edited_property() const;
|
||||
inline Variant get_edited_property_value() const { return object->get(property); }
|
||||
inline Variant get_edited_property_value() const {
|
||||
ERR_FAIL_NULL_V(object, Variant());
|
||||
return object->get(property);
|
||||
}
|
||||
EditorInspector *get_parent_inspector() const;
|
||||
|
||||
void set_doc_path(const String &p_doc_path);
|
||||
|
|
@ -217,6 +236,9 @@ public:
|
|||
void set_name_split_ratio(float p_ratio);
|
||||
float get_name_split_ratio() const;
|
||||
|
||||
void set_favoritable(bool p_favoritable);
|
||||
bool is_favoritable() const;
|
||||
|
||||
void set_object_and_property(Object *p_object, const StringName &p_property);
|
||||
virtual Control *make_custom_tooltip(const String &p_text) const override;
|
||||
|
||||
|
|
@ -284,6 +306,7 @@ class EditorInspectorCategory : public Control {
|
|||
String label;
|
||||
String doc_class_name;
|
||||
PopupMenu *menu = nullptr;
|
||||
bool is_favorite = false;
|
||||
|
||||
void _handle_menu_option(int p_option);
|
||||
|
||||
|
|
@ -292,10 +315,10 @@ protected:
|
|||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
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 {
|
||||
|
|
@ -310,7 +333,6 @@ class EditorInspectorSection : public Container {
|
|||
int level = 1;
|
||||
|
||||
Timer *dropping_unfold_timer = nullptr;
|
||||
bool dropping = false;
|
||||
bool dropping_for_unfold = false;
|
||||
|
||||
HashSet<StringName> revertable_properties;
|
||||
|
|
@ -331,6 +353,7 @@ 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;
|
||||
VBoxContainer *get_vbox();
|
||||
void unfold();
|
||||
void fold();
|
||||
|
|
@ -376,6 +399,7 @@ class EditorInspectorArray : public EditorInspectorSection {
|
|||
|
||||
bool read_only = false;
|
||||
bool movable = true;
|
||||
bool is_const = false;
|
||||
bool numbered = false;
|
||||
|
||||
enum MenuOptions {
|
||||
|
|
@ -442,8 +466,8 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void setup_with_move_element_function(Object *p_object, const String &p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "");
|
||||
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_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");
|
||||
void setup_with_move_element_function(Object *p_object, const String &p_label, 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 = "");
|
||||
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);
|
||||
|
||||
EditorInspectorArray(bool p_read_only);
|
||||
|
|
@ -480,12 +504,32 @@ public:
|
|||
class EditorInspector : public ScrollContainer {
|
||||
GDCLASS(EditorInspector, ScrollContainer);
|
||||
|
||||
friend class EditorInspectorCategory;
|
||||
friend class EditorPropertyResource;
|
||||
|
||||
enum {
|
||||
MAX_PLUGINS = 1024
|
||||
};
|
||||
static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS];
|
||||
static int inspector_plugin_count;
|
||||
|
||||
// Right-click context menu options.
|
||||
enum ClassMenuOption {
|
||||
MENU_UNFAVORITE_ALL,
|
||||
};
|
||||
|
||||
bool can_favorite = false;
|
||||
PackedStringArray current_favorites;
|
||||
VBoxContainer *favorites_section = nullptr;
|
||||
EditorInspectorCategory *favorites_category = nullptr;
|
||||
VBoxContainer *favorites_vbox = nullptr;
|
||||
VBoxContainer *favorites_groups_vbox = nullptr;
|
||||
HSeparator *favorites_separator = nullptr;
|
||||
|
||||
EditorInspector *root_inspector = nullptr;
|
||||
|
||||
VBoxContainer *base_vbox = nullptr;
|
||||
VBoxContainer *begin_vbox = nullptr;
|
||||
VBoxContainer *main_vbox = nullptr;
|
||||
|
||||
// Map used to cache the instantiated editors.
|
||||
|
|
@ -514,7 +558,6 @@ class EditorInspector : public ScrollContainer {
|
|||
bool update_all_pending = false;
|
||||
bool read_only = false;
|
||||
bool keying = false;
|
||||
bool sub_inspector = false;
|
||||
bool wide_editors = false;
|
||||
bool deletable_properties = false;
|
||||
|
||||
|
|
@ -552,12 +595,18 @@ class EditorInspector : public ScrollContainer {
|
|||
void _property_checked(const String &p_path, bool p_checked);
|
||||
void _property_pinned(const String &p_path, bool p_pinned);
|
||||
bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style);
|
||||
bool _resource_properties_matches(const Ref<Resource> &p_resource, const String &p_filter);
|
||||
|
||||
void _resource_selected(const String &p_path, Ref<Resource> p_resource);
|
||||
void _property_selected(const String &p_path, int p_focusable);
|
||||
void _object_id_selected(const String &p_path, ObjectID p_id);
|
||||
|
||||
void _update_current_favorites();
|
||||
void _set_property_favorited(const String &p_path, bool p_favorited);
|
||||
void _clear_current_favorites();
|
||||
|
||||
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);
|
||||
|
|
@ -567,7 +616,6 @@ class EditorInspector : public ScrollContainer {
|
|||
|
||||
void _keying_changed();
|
||||
|
||||
void _filter_changed(const String &p_text);
|
||||
void _parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped);
|
||||
|
||||
void _vscroll_changed(double);
|
||||
|
|
@ -576,14 +624,15 @@ class EditorInspector : public ScrollContainer {
|
|||
|
||||
bool _is_property_disabled_by_feature_profile(const StringName &p_property);
|
||||
|
||||
ConfirmationDialog *add_meta_dialog = nullptr;
|
||||
AddMetadataDialog *add_meta_dialog = nullptr;
|
||||
LineEdit *add_meta_name = nullptr;
|
||||
OptionButton *add_meta_type = nullptr;
|
||||
EditorValidationPanel *validation_panel = nullptr;
|
||||
|
||||
void _add_meta_confirm();
|
||||
void _show_add_meta_dialog();
|
||||
void _check_meta_name();
|
||||
|
||||
void _handle_menu_option(int p_option);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
|
@ -645,8 +694,9 @@ public:
|
|||
String get_object_class() const;
|
||||
|
||||
void set_use_wide_editors(bool p_enable);
|
||||
void set_sub_inspector(bool p_enable);
|
||||
bool is_sub_inspector() const { return sub_inspector; }
|
||||
void set_root_inspector(EditorInspector *p_root_inspector);
|
||||
EditorInspector *get_root_inspector() { return is_sub_inspector() ? root_inspector : this; }
|
||||
bool is_sub_inspector() const { return root_inspector != nullptr; }
|
||||
|
||||
void set_use_deletable_properties(bool p_enabled);
|
||||
|
||||
|
|
|
|||
53
engine/editor/editor_interface.compat.inc
Normal file
53
engine/editor/editor_interface.compat.inc
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**************************************************************************/
|
||||
/* editor_interface.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_interface.h"
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void EditorInterface::_popup_node_selector_bind_compat_94323(const Callable &p_callback, const TypedArray<StringName> &p_valid_types) {
|
||||
popup_node_selector(p_callback, p_valid_types, nullptr);
|
||||
}
|
||||
|
||||
void EditorInterface::_popup_property_selector_bind_compat_94323(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter) {
|
||||
popup_property_selector(p_object, p_callback, p_type_filter, String());
|
||||
}
|
||||
|
||||
void EditorInterface::_open_scene_from_path_bind_compat_90057(const String &scene_path) {
|
||||
return open_scene_from_path(scene_path, false);
|
||||
}
|
||||
|
||||
void EditorInterface::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("popup_node_selector", "callback", "valid_types"), &EditorInterface::_popup_node_selector_bind_compat_94323, DEFVAL(TypedArray<StringName>()));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter"), &EditorInterface::_popup_property_selector_bind_compat_94323, DEFVAL(PackedInt32Array()));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("open_scene_from_path", "scene_path"), &EditorInterface::_open_scene_from_path_bind_compat_90057);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -29,23 +29,32 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "editor_interface.h"
|
||||
#include "editor_interface.compat.inc"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/create_dialog.h"
|
||||
#include "editor/editor_command_palette.h"
|
||||
#include "editor/editor_feature_profile.h"
|
||||
#include "editor/editor_main_screen.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/filesystem_dock.h"
|
||||
#include "editor/gui/editor_quick_open_dialog.h"
|
||||
#include "editor/gui/editor_run_bar.h"
|
||||
#include "editor/gui/editor_scene_tabs.h"
|
||||
#include "editor/gui/editor_toaster.h"
|
||||
#include "editor/gui/scene_tree_editor.h"
|
||||
#include "editor/inspector_dock.h"
|
||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||
#include "editor/property_selector.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "main/main.h"
|
||||
#include "plugins/editor_preview_plugins.h"
|
||||
#include "scene/3d/light_3d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/main/window.h"
|
||||
|
|
@ -86,6 +95,35 @@ Ref<EditorSettings> EditorInterface::get_editor_settings() const {
|
|||
return EditorSettings::get_singleton();
|
||||
}
|
||||
|
||||
EditorToaster *EditorInterface::get_editor_toaster() const {
|
||||
return EditorToaster::get_singleton();
|
||||
}
|
||||
|
||||
EditorUndoRedoManager *EditorInterface::get_editor_undo_redo() const {
|
||||
return EditorUndoRedoManager::get_singleton();
|
||||
}
|
||||
|
||||
AABB EditorInterface::_calculate_aabb_for_scene(Node *p_node, AABB &p_scene_aabb) {
|
||||
MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node);
|
||||
if (mesh_node && mesh_node->get_mesh().is_valid()) {
|
||||
Transform3D accum_xform;
|
||||
Node3D *base = mesh_node;
|
||||
while (base) {
|
||||
accum_xform = base->get_transform() * accum_xform;
|
||||
base = Object::cast_to<Node3D>(base->get_parent());
|
||||
}
|
||||
|
||||
AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb());
|
||||
p_scene_aabb.merge_with(aabb);
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
p_scene_aabb = _calculate_aabb_for_scene(p_node->get_child(i), p_scene_aabb);
|
||||
}
|
||||
|
||||
return p_scene_aabb;
|
||||
}
|
||||
|
||||
TypedArray<Texture2D> EditorInterface::_make_mesh_previews(const TypedArray<Mesh> &p_meshes, int p_preview_size) {
|
||||
Vector<Ref<Mesh>> meshes;
|
||||
|
||||
|
|
@ -131,7 +169,7 @@ Vector<Ref<Texture2D>> EditorInterface::make_mesh_previews(const Vector<Ref<Mesh
|
|||
|
||||
for (int i = 0; i < p_meshes.size(); i++) {
|
||||
const Ref<Mesh> &mesh = p_meshes[i];
|
||||
if (!mesh.is_valid()) {
|
||||
if (mesh.is_null()) {
|
||||
textures.push_back(Ref<Texture2D>());
|
||||
continue;
|
||||
}
|
||||
|
|
@ -172,7 +210,7 @@ Vector<Ref<Texture2D>> EditorInterface::make_mesh_previews(const Vector<Ref<Mesh
|
|||
Main::iteration();
|
||||
Main::iteration();
|
||||
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
|
||||
ERR_CONTINUE(!img.is_valid() || img->is_empty());
|
||||
ERR_CONTINUE(img.is_null() || img->is_empty());
|
||||
Ref<ImageTexture> it = ImageTexture::create_from_image(img);
|
||||
|
||||
RS::get_singleton()->free(inst);
|
||||
|
|
@ -191,6 +229,137 @@ Vector<Ref<Texture2D>> EditorInterface::make_mesh_previews(const Vector<Ref<Mesh
|
|||
return textures;
|
||||
}
|
||||
|
||||
void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, int p_preview_size) {
|
||||
ERR_FAIL_NULL_MSG(p_scene, "The provided scene is null.");
|
||||
ERR_FAIL_COND_MSG(p_scene->is_inside_tree(), "The scene must not be inside the tree.");
|
||||
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be called from the editor.");
|
||||
ERR_FAIL_NULL_MSG(EditorNode::get_singleton(), "EditorNode doesn't exist.");
|
||||
|
||||
SubViewport *sub_viewport_node = memnew(SubViewport);
|
||||
AABB scene_aabb;
|
||||
scene_aabb = _calculate_aabb_for_scene(p_scene, scene_aabb);
|
||||
|
||||
sub_viewport_node->set_update_mode(SubViewport::UPDATE_ALWAYS);
|
||||
sub_viewport_node->set_size(Vector2i(p_preview_size, p_preview_size));
|
||||
sub_viewport_node->set_transparent_background(false);
|
||||
Ref<World3D> world;
|
||||
world.instantiate();
|
||||
sub_viewport_node->set_world_3d(world);
|
||||
|
||||
EditorNode::get_singleton()->add_child(sub_viewport_node);
|
||||
Ref<Environment> env;
|
||||
env.instantiate();
|
||||
env->set_background(Environment::BG_CLEAR_COLOR);
|
||||
|
||||
Ref<CameraAttributesPractical> camera_attributes;
|
||||
camera_attributes.instantiate();
|
||||
|
||||
Node3D *root = memnew(Node3D);
|
||||
root->set_name("Root");
|
||||
sub_viewport_node->add_child(root);
|
||||
|
||||
Camera3D *camera = memnew(Camera3D);
|
||||
camera->set_environment(env);
|
||||
camera->set_attributes(camera_attributes);
|
||||
camera->set_name("Camera3D");
|
||||
root->add_child(camera);
|
||||
camera->set_current(true);
|
||||
|
||||
camera->set_position(Vector3(0.0, 0.0, 3.0));
|
||||
|
||||
DirectionalLight3D *light = memnew(DirectionalLight3D);
|
||||
light->set_name("Light");
|
||||
DirectionalLight3D *light2 = memnew(DirectionalLight3D);
|
||||
light2->set_name("Light2");
|
||||
light2->set_color(Color(0.7, 0.7, 0.7, 1.0));
|
||||
|
||||
root->add_child(light);
|
||||
root->add_child(light2);
|
||||
|
||||
sub_viewport_node->add_child(p_scene);
|
||||
|
||||
// Calculate the camera and lighting position based on the size of the scene.
|
||||
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;
|
||||
|
||||
camera->set_orthogonal(camera_size * 2.0, 0.0001, camera_size * 2.0);
|
||||
|
||||
Transform3D xf;
|
||||
xf.basis = Basis(Vector3(0, 1, 0), cam_rot_y) * Basis(Vector3(1, 0, 0), cam_rot_x);
|
||||
xf.origin = center;
|
||||
xf.translate_local(0, 0, camera_size);
|
||||
|
||||
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;
|
||||
|
||||
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)));
|
||||
|
||||
// Update the renderer to get the screenshot.
|
||||
DisplayServer::get_singleton()->process_events();
|
||||
Main::iteration();
|
||||
Main::iteration();
|
||||
|
||||
// Get the texture.
|
||||
Ref<Texture2D> texture = sub_viewport_node->get_texture();
|
||||
ERR_FAIL_COND_MSG(texture.is_null(), "Failed to get texture from sub_viewport_node.");
|
||||
|
||||
// Remove the initial scene node.
|
||||
sub_viewport_node->remove_child(p_scene);
|
||||
|
||||
// Cleanup the viewport.
|
||||
if (sub_viewport_node) {
|
||||
if (sub_viewport_node->get_parent()) {
|
||||
sub_viewport_node->get_parent()->remove_child(sub_viewport_node);
|
||||
}
|
||||
sub_viewport_node->queue_free();
|
||||
sub_viewport_node = nullptr;
|
||||
}
|
||||
|
||||
// Now generate the cache image.
|
||||
Ref<Image> img = texture->get_image();
|
||||
if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) {
|
||||
img = img->duplicate();
|
||||
|
||||
int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
|
||||
preview_size *= EDSCALE;
|
||||
|
||||
int vp_size = MIN(img->get_width(), img->get_height());
|
||||
int x = (img->get_width() - vp_size) / 2;
|
||||
int y = (img->get_height() - vp_size) / 2;
|
||||
|
||||
if (vp_size < preview_size) {
|
||||
img->crop_from_point(x, y, vp_size, vp_size);
|
||||
} else {
|
||||
int ratio = vp_size / preview_size;
|
||||
int size = preview_size * MAX(1, ratio / 2);
|
||||
|
||||
x = (img->get_width() - size) / 2;
|
||||
y = (img->get_height() - size) / 2;
|
||||
|
||||
img->crop_from_point(x, y, size, size);
|
||||
img->resize(preview_size, preview_size, Image::INTERPOLATE_LANCZOS);
|
||||
}
|
||||
img->convert(Image::FORMAT_RGB8);
|
||||
|
||||
String temp_path = EditorPaths::get_singleton()->get_cache_dir();
|
||||
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
|
||||
cache_base = temp_path.path_join("resthumb-" + cache_base);
|
||||
|
||||
post_process_preview(img);
|
||||
img->save_png(cache_base + ".png");
|
||||
}
|
||||
|
||||
EditorResourcePreview::get_singleton()->check_for_invalidation(p_path);
|
||||
EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed"));
|
||||
}
|
||||
|
||||
void EditorInterface::set_plugin_enabled(const String &p_plugin, bool p_enabled) {
|
||||
EditorNode::get_singleton()->set_addon_plugin_enabled(p_plugin, p_enabled, true);
|
||||
}
|
||||
|
|
@ -210,7 +379,7 @@ Control *EditorInterface::get_base_control() const {
|
|||
}
|
||||
|
||||
VBoxContainer *EditorInterface::get_editor_main_screen() const {
|
||||
return EditorNode::get_singleton()->get_main_screen_control();
|
||||
return EditorNode::get_singleton()->get_editor_main_screen()->get_control();
|
||||
}
|
||||
|
||||
ScriptEditor *EditorInterface::get_script_editor() const {
|
||||
|
|
@ -227,7 +396,7 @@ SubViewport *EditorInterface::get_editor_viewport_3d(int p_idx) const {
|
|||
}
|
||||
|
||||
void EditorInterface::set_main_screen_editor(const String &p_name) {
|
||||
EditorNode::get_singleton()->select_editor_by_name(p_name);
|
||||
EditorNode::get_singleton()->get_editor_main_screen()->select_by_name(p_name);
|
||||
}
|
||||
|
||||
void EditorInterface::set_distraction_free_mode(bool p_enter) {
|
||||
|
|
@ -272,15 +441,11 @@ void EditorInterface::set_current_feature_profile(const String &p_profile_name)
|
|||
|
||||
// Editor dialogs.
|
||||
|
||||
void EditorInterface::popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types) {
|
||||
// TODO: Should reuse dialog instance instead of creating a fresh one, but need to rework set_valid_types first.
|
||||
if (node_selector) {
|
||||
node_selector->disconnect(SNAME("selected"), callable_mp(this, &EditorInterface::_node_selected).bind(p_callback));
|
||||
node_selector->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_node_selection_canceled).bind(p_callback));
|
||||
get_base_control()->remove_child(node_selector);
|
||||
node_selector->queue_free();
|
||||
void EditorInterface::popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types, Node *p_current_value) {
|
||||
if (!node_selector) {
|
||||
node_selector = memnew(SceneTreeDialog);
|
||||
get_base_control()->add_child(node_selector);
|
||||
}
|
||||
node_selector = memnew(SceneTreeDialog);
|
||||
|
||||
Vector<StringName> valid_types;
|
||||
int length = p_valid_types.size();
|
||||
|
|
@ -289,27 +454,18 @@ void EditorInterface::popup_node_selector(const Callable &p_callback, const Type
|
|||
valid_types.write[i] = p_valid_types[i];
|
||||
}
|
||||
node_selector->set_valid_types(valid_types);
|
||||
node_selector->popup_scenetree_dialog(p_current_value);
|
||||
|
||||
get_base_control()->add_child(node_selector);
|
||||
|
||||
node_selector->popup_scenetree_dialog();
|
||||
|
||||
const Callable selected_callback = callable_mp(this, &EditorInterface::_node_selected).bind(p_callback);
|
||||
node_selector->connect(SNAME("selected"), selected_callback, CONNECT_DEFERRED);
|
||||
|
||||
const Callable canceled_callback = callable_mp(this, &EditorInterface::_node_selection_canceled).bind(p_callback);
|
||||
node_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED);
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_node_selected);
|
||||
node_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED);
|
||||
node_selector->connect(SNAME("canceled"), callback.bind(NodePath(), p_callback), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
void EditorInterface::popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter) {
|
||||
// TODO: Should reuse dialog instance instead of creating a fresh one, but need to rework set_type_filter first.
|
||||
if (property_selector) {
|
||||
property_selector->disconnect(SNAME("selected"), callable_mp(this, &EditorInterface::_property_selected).bind(p_callback));
|
||||
property_selector->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_property_selection_canceled).bind(p_callback));
|
||||
get_base_control()->remove_child(property_selector);
|
||||
property_selector->queue_free();
|
||||
void EditorInterface::popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter, const String &p_current_value) {
|
||||
if (!property_selector) {
|
||||
property_selector = memnew(PropertySelector);
|
||||
get_base_control()->add_child(property_selector);
|
||||
}
|
||||
property_selector = memnew(PropertySelector);
|
||||
|
||||
Vector<Variant::Type> type_filter;
|
||||
int length = p_type_filter.size();
|
||||
|
|
@ -318,33 +474,119 @@ void EditorInterface::popup_property_selector(Object *p_object, const Callable &
|
|||
type_filter.write[i] = (Variant::Type)p_type_filter[i];
|
||||
}
|
||||
property_selector->set_type_filter(type_filter);
|
||||
property_selector->select_property_from_instance(p_object, p_current_value);
|
||||
|
||||
get_base_control()->add_child(property_selector);
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_property_selected);
|
||||
property_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED);
|
||||
property_selector->connect(SNAME("canceled"), callback.bind(String(), p_callback), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
property_selector->select_property_from_instance(p_object);
|
||||
void EditorInterface::popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value) {
|
||||
if (!method_selector) {
|
||||
method_selector = memnew(PropertySelector);
|
||||
get_base_control()->add_child(method_selector);
|
||||
}
|
||||
|
||||
const Callable selected_callback = callable_mp(this, &EditorInterface::_property_selected).bind(p_callback);
|
||||
property_selector->connect(SNAME("selected"), selected_callback, CONNECT_DEFERRED);
|
||||
method_selector->select_method_from_instance(p_object, p_current_value);
|
||||
|
||||
const Callable canceled_callback = callable_mp(this, &EditorInterface::_property_selection_canceled).bind(p_callback);
|
||||
property_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED);
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_method_selected);
|
||||
method_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED);
|
||||
method_selector->connect(SNAME("canceled"), callback.bind(String(), p_callback), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types) {
|
||||
StringName required_type = SNAME("Resource");
|
||||
Vector<StringName> base_types;
|
||||
if (p_base_types.is_empty()) {
|
||||
base_types.append(required_type);
|
||||
} else {
|
||||
for (int i = 0; i < p_base_types.size(); i++) {
|
||||
StringName type = p_base_types[i];
|
||||
ERR_FAIL_COND_MSG(!(ClassDB::is_parent_class(type, required_type) || EditorNode::get_editor_data().script_class_is_parent(type, required_type)), "Only types deriving from Resource are supported in the quick open dialog.");
|
||||
base_types.append(type);
|
||||
}
|
||||
}
|
||||
|
||||
EditorQuickOpenDialog *quick_open = EditorNode::get_singleton()->get_quick_open_dialog();
|
||||
quick_open->connect(SNAME("canceled"), callable_mp(this, &EditorInterface::_quick_open).bind(String(), p_callback));
|
||||
quick_open->popup_dialog(base_types, callable_mp(this, &EditorInterface::_quick_open).bind(p_callback));
|
||||
}
|
||||
|
||||
void EditorInterface::popup_create_dialog(const Callable &p_callback, const StringName &p_base_type, const String &p_current_type, const String &p_dialog_title, const TypedArray<StringName> &p_custom_type_blocklist) {
|
||||
if (!create_dialog) {
|
||||
create_dialog = memnew(CreateDialog);
|
||||
get_base_control()->add_child(create_dialog);
|
||||
}
|
||||
|
||||
HashSet<StringName> blocklist;
|
||||
for (const Variant &E : p_custom_type_blocklist) {
|
||||
blocklist.insert(E);
|
||||
}
|
||||
create_dialog->set_type_blocklist(blocklist);
|
||||
|
||||
String safe_base_type = p_base_type;
|
||||
if (p_base_type.is_empty() || (!ClassDB::class_exists(p_base_type) && !ScriptServer::is_global_class(p_base_type))) {
|
||||
ERR_PRINT(vformat("Invalid base type '%s'. The base type has fallen back to 'Object'.", p_base_type));
|
||||
safe_base_type = "Object";
|
||||
}
|
||||
|
||||
create_dialog->set_base_type(safe_base_type);
|
||||
create_dialog->popup_create(false, true, p_current_type, "");
|
||||
create_dialog->set_title(p_dialog_title.is_empty() ? vformat(TTR("Create New %s"), p_base_type) : p_dialog_title);
|
||||
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_create_dialog_item_selected);
|
||||
create_dialog->connect(SNAME("create"), callback.bind(false, p_callback), CONNECT_DEFERRED);
|
||||
create_dialog->connect(SNAME("canceled"), callback.bind(true, p_callback), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
void EditorInterface::_node_selected(const NodePath &p_node_path, const Callable &p_callback) {
|
||||
const NodePath path = get_edited_scene_root()->get_path().rel_path_to(p_node_path);
|
||||
_call_dialog_callback(p_callback, path, "node selected");
|
||||
}
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_node_selected);
|
||||
node_selector->disconnect(SNAME("selected"), callback);
|
||||
node_selector->disconnect(SNAME("canceled"), callback);
|
||||
|
||||
void EditorInterface::_node_selection_canceled(const Callable &p_callback) {
|
||||
_call_dialog_callback(p_callback, NodePath(), "node selection canceled");
|
||||
if (p_node_path.is_empty()) {
|
||||
_call_dialog_callback(p_callback, NodePath(), "node selection canceled");
|
||||
} else {
|
||||
const NodePath path = get_edited_scene_root()->get_path().rel_path_to(p_node_path);
|
||||
_call_dialog_callback(p_callback, path, "node selected");
|
||||
}
|
||||
}
|
||||
|
||||
void EditorInterface::_property_selected(const String &p_property_name, const Callable &p_callback) {
|
||||
_call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selected");
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_property_selected);
|
||||
property_selector->disconnect(SNAME("selected"), callback);
|
||||
property_selector->disconnect(SNAME("canceled"), callback);
|
||||
|
||||
if (p_property_name.is_empty()) {
|
||||
_call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selection canceled");
|
||||
} else {
|
||||
_call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selected");
|
||||
}
|
||||
}
|
||||
|
||||
void EditorInterface::_property_selection_canceled(const Callable &p_callback) {
|
||||
_call_dialog_callback(p_callback, NodePath(), "property selection canceled");
|
||||
void EditorInterface::_method_selected(const String &p_method_name, const Callable &p_callback) {
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_method_selected);
|
||||
method_selector->disconnect(SNAME("selected"), callback);
|
||||
method_selector->disconnect(SNAME("canceled"), callback);
|
||||
|
||||
if (p_method_name.is_empty()) {
|
||||
_call_dialog_callback(p_callback, p_method_name, "method selection canceled");
|
||||
} else {
|
||||
_call_dialog_callback(p_callback, p_method_name, "method selected");
|
||||
}
|
||||
}
|
||||
|
||||
void EditorInterface::_quick_open(const String &p_file_path, const Callable &p_callback) {
|
||||
EditorQuickOpenDialog *quick_open = EditorNode::get_singleton()->get_quick_open_dialog();
|
||||
quick_open->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_quick_open));
|
||||
_call_dialog_callback(p_callback, p_file_path, "quick open");
|
||||
}
|
||||
|
||||
void EditorInterface::_create_dialog_item_selected(bool p_is_canceled, const Callable &p_callback) {
|
||||
const Callable callback = callable_mp(this, &EditorInterface::_create_dialog_item_selected);
|
||||
create_dialog->disconnect(SNAME("create"), callback);
|
||||
create_dialog->disconnect(SNAME("canceled"), callback);
|
||||
_call_dialog_callback(p_callback, p_is_canceled ? "" : create_dialog->get_selected_type(), "create dialog");
|
||||
}
|
||||
|
||||
void EditorInterface::_call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context) {
|
||||
|
|
@ -401,12 +643,12 @@ void EditorInterface::edit_script(const Ref<Script> &p_script, int p_line, int p
|
|||
ScriptEditor::get_singleton()->edit(p_script, p_line - 1, p_col - 1, p_grab_focus);
|
||||
}
|
||||
|
||||
void EditorInterface::open_scene_from_path(const String &scene_path) {
|
||||
void EditorInterface::open_scene_from_path(const String &scene_path, bool p_set_inherited) {
|
||||
if (EditorNode::get_singleton()->is_changing_scene()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->open_request(scene_path);
|
||||
EditorNode::get_singleton()->open_request(scene_path, p_set_inherited);
|
||||
}
|
||||
|
||||
void EditorInterface::reload_scene_from_path(const String &scene_path) {
|
||||
|
|
@ -525,6 +767,8 @@ void EditorInterface::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_resource_previewer"), &EditorInterface::get_resource_previewer);
|
||||
ClassDB::bind_method(D_METHOD("get_selection"), &EditorInterface::get_selection);
|
||||
ClassDB::bind_method(D_METHOD("get_editor_settings"), &EditorInterface::get_editor_settings);
|
||||
ClassDB::bind_method(D_METHOD("get_editor_toaster"), &EditorInterface::get_editor_toaster);
|
||||
ClassDB::bind_method(D_METHOD("get_editor_undo_redo"), &EditorInterface::get_editor_undo_redo);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("make_mesh_previews", "meshes", "preview_size"), &EditorInterface::_make_mesh_previews);
|
||||
|
||||
|
|
@ -559,8 +803,11 @@ void EditorInterface::_bind_methods() {
|
|||
|
||||
// Editor dialogs.
|
||||
|
||||
ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray<StringName>()));
|
||||
ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()));
|
||||
ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types", "current_value"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray<StringName>()), DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String()));
|
||||
ClassDB::bind_method(D_METHOD("popup_method_selector", "object", "callback", "current_value"), &EditorInterface::popup_method_selector, DEFVAL(String()));
|
||||
ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray<StringName>()));
|
||||
ClassDB::bind_method(D_METHOD("popup_create_dialog", "callback", "base_type", "current_type", "dialog_title", "type_blocklist"), &EditorInterface::popup_create_dialog, DEFVAL(""), DEFVAL(""), DEFVAL(""), DEFVAL(TypedArray<StringName>()));
|
||||
|
||||
// Editor docks.
|
||||
|
||||
|
|
@ -579,7 +826,7 @@ void EditorInterface::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("edit_resource", "resource"), &EditorInterface::edit_resource);
|
||||
ClassDB::bind_method(D_METHOD("edit_node", "node"), &EditorInterface::edit_node);
|
||||
ClassDB::bind_method(D_METHOD("edit_script", "script", "line", "column", "grab_focus"), &EditorInterface::edit_script, DEFVAL(-1), DEFVAL(0), DEFVAL(true));
|
||||
ClassDB::bind_method(D_METHOD("open_scene_from_path", "scene_filepath"), &EditorInterface::open_scene_from_path);
|
||||
ClassDB::bind_method(D_METHOD("open_scene_from_path", "scene_filepath", "set_inherited"), &EditorInterface::open_scene_from_path, DEFVAL(false));
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "core/object/script_language.h"
|
||||
|
||||
class Control;
|
||||
class CreateDialog;
|
||||
class EditorCommandPalette;
|
||||
class EditorFileSystem;
|
||||
class EditorInspector;
|
||||
|
|
@ -45,6 +46,8 @@ class EditorPlugin;
|
|||
class EditorResourcePreview;
|
||||
class EditorSelection;
|
||||
class EditorSettings;
|
||||
class EditorToaster;
|
||||
class EditorUndoRedoManager;
|
||||
class FileSystemDock;
|
||||
class Mesh;
|
||||
class Node;
|
||||
|
|
@ -65,21 +68,32 @@ class EditorInterface : public Object {
|
|||
// Editor dialogs.
|
||||
|
||||
PropertySelector *property_selector = nullptr;
|
||||
PropertySelector *method_selector = nullptr;
|
||||
SceneTreeDialog *node_selector = nullptr;
|
||||
CreateDialog *create_dialog = nullptr;
|
||||
|
||||
void _node_selected(const NodePath &p_node_paths, const Callable &p_callback);
|
||||
void _node_selection_canceled(const Callable &p_callback);
|
||||
void _property_selected(const String &p_property_name, const Callable &p_callback);
|
||||
void _property_selection_canceled(const Callable &p_callback);
|
||||
void _method_selected(const String &p_property_name, const Callable &p_callback);
|
||||
void _quick_open(const String &p_file_path, const Callable &p_callback);
|
||||
void _create_dialog_item_selected(bool p_is_canceled, const Callable &p_callback);
|
||||
void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context);
|
||||
|
||||
// Editor tools.
|
||||
|
||||
TypedArray<Texture2D> _make_mesh_previews(const TypedArray<Mesh> &p_meshes, int p_preview_size);
|
||||
AABB _calculate_aabb_for_scene(Node *p_node, AABB &p_scene_aabb);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _popup_node_selector_bind_compat_94323(const Callable &p_callback, const TypedArray<StringName> &p_valid_types = TypedArray<StringName>());
|
||||
void _popup_property_selector_bind_compat_94323(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array());
|
||||
void _open_scene_from_path_bind_compat_90057(const String &scene_path);
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
static EditorInterface *get_singleton() { return singleton; }
|
||||
|
||||
|
|
@ -93,8 +107,11 @@ public:
|
|||
EditorResourcePreview *get_resource_previewer() const;
|
||||
EditorSelection *get_selection() const;
|
||||
Ref<EditorSettings> get_editor_settings() const;
|
||||
EditorToaster *get_editor_toaster() const;
|
||||
EditorUndoRedoManager *get_editor_undo_redo() const;
|
||||
|
||||
Vector<Ref<Texture2D>> make_mesh_previews(const Vector<Ref<Mesh>> &p_meshes, Vector<Transform3D> *p_transforms, int p_preview_size);
|
||||
void make_scene_preview(const String &p_path, Node *p_scene, int p_preview_size);
|
||||
|
||||
void set_plugin_enabled(const String &p_plugin, bool p_enabled);
|
||||
bool is_plugin_enabled(const String &p_plugin) const;
|
||||
|
|
@ -126,9 +143,12 @@ public:
|
|||
|
||||
// Editor dialogs.
|
||||
|
||||
void popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types = TypedArray<StringName>());
|
||||
void popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types = TypedArray<StringName>(), Node *p_current_value = nullptr);
|
||||
// Must use Vector<int> because exposing Vector<Variant::Type> is not supported.
|
||||
void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array());
|
||||
void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String());
|
||||
void popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value = String());
|
||||
void popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types = TypedArray<StringName>());
|
||||
void popup_create_dialog(const Callable &p_callback, const StringName &p_base_type = "", const String &p_current_type = "", const String &p_dialog_title = "", const TypedArray<StringName> &p_custom_type_blocklist = TypedArray<StringName>());
|
||||
|
||||
// Editor docks.
|
||||
|
||||
|
|
@ -147,7 +167,7 @@ public:
|
|||
void edit_resource(const Ref<Resource> &p_resource);
|
||||
void edit_node(Node *p_node);
|
||||
void edit_script(const Ref<Script> &p_script, int p_line = -1, int p_col = 0, bool p_grab_focus = true);
|
||||
void open_scene_from_path(const String &scene_path);
|
||||
void open_scene_from_path(const String &scene_path, bool p_set_inherited = false);
|
||||
void reload_scene_from_path(const String &scene_path);
|
||||
|
||||
PackedStringArray get_open_scenes() const;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "editor_locale_dialog.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "editor/editor_undo_redo_manager.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/check_button.h"
|
||||
|
|
@ -407,7 +408,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
|
|||
edit_filters->set_text(TTR("Edit Filters"));
|
||||
edit_filters->set_toggle_mode(true);
|
||||
edit_filters->set_pressed(false);
|
||||
edit_filters->connect("toggled", callable_mp(this, &EditorLocaleDialog::_edit_filters));
|
||||
edit_filters->connect(SceneStringName(toggled), callable_mp(this, &EditorLocaleDialog::_edit_filters));
|
||||
hb_filter->add_child(edit_filters);
|
||||
}
|
||||
{
|
||||
|
|
@ -415,7 +416,7 @@ EditorLocaleDialog::EditorLocaleDialog() {
|
|||
advanced->set_text(TTR("Advanced"));
|
||||
advanced->set_toggle_mode(true);
|
||||
advanced->set_pressed(false);
|
||||
advanced->connect("toggled", callable_mp(this, &EditorLocaleDialog::_toggle_advanced));
|
||||
advanced->connect(SceneStringName(toggled), callable_mp(this, &EditorLocaleDialog::_toggle_advanced));
|
||||
hb_filter->add_child(advanced);
|
||||
}
|
||||
vb->add_child(hb_filter);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef EDITOR_LOCALE_DIALOG_H
|
||||
#define EDITOR_LOCALE_DIALOG_H
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
class Button;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/center_container.h"
|
||||
#include "scene/gui/separator.h"
|
||||
#include "scene/resources/font.h"
|
||||
|
||||
|
|
@ -52,10 +51,6 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
|
|||
err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error);
|
||||
}
|
||||
|
||||
if (p_editor_notify) {
|
||||
err_str += " (User)";
|
||||
}
|
||||
|
||||
MessageType message_type = p_type == ERR_HANDLER_WARNING ? MSG_TYPE_WARNING : MSG_TYPE_ERROR;
|
||||
|
||||
if (!Thread::is_main_thread()) {
|
||||
|
|
@ -104,20 +99,20 @@ void EditorLog::_update_theme() {
|
|||
log->add_theme_font_size_override("mono_font_size", font_size);
|
||||
log->end_bulk_theme_override();
|
||||
|
||||
type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_editor_theme_icon(SNAME("Popup")));
|
||||
type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_editor_theme_icon(SNAME("StatusError")));
|
||||
type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_editor_theme_icon(SNAME("StatusWarning")));
|
||||
type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
type_filter_map[MSG_TYPE_STD]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("Popup")));
|
||||
type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError")));
|
||||
type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("StatusWarning")));
|
||||
type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
|
||||
type_filter_map[MSG_TYPE_STD]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_theme_type_variation("EditorLogFilterButton");
|
||||
|
||||
clear_button->set_icon(get_editor_theme_icon(SNAME("Clear")));
|
||||
copy_button->set_icon(get_editor_theme_icon(SNAME("ActionCopy")));
|
||||
collapse_button->set_icon(get_editor_theme_icon(SNAME("CombineLines")));
|
||||
show_search_button->set_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear")));
|
||||
copy_button->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy")));
|
||||
collapse_button->set_button_icon(get_editor_theme_icon(SNAME("CombineLines")));
|
||||
show_search_button->set_button_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
|
||||
theme_cache.error_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
|
||||
|
|
@ -208,7 +203,7 @@ void EditorLog::_clear_request() {
|
|||
log->clear();
|
||||
messages.clear();
|
||||
_reset_message_counts();
|
||||
tool_button->set_icon(Ref<Texture2D>());
|
||||
tool_button->set_button_icon(Ref<Texture2D>());
|
||||
}
|
||||
|
||||
void EditorLog::_copy_request() {
|
||||
|
|
@ -362,15 +357,19 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
|
|||
log->push_color(theme_cache.error_color);
|
||||
Ref<Texture2D> icon = theme_cache.error_icon;
|
||||
log->add_image(icon);
|
||||
log->add_text(" ");
|
||||
tool_button->set_icon(icon);
|
||||
log->push_bold();
|
||||
log->add_text(" ERROR: ");
|
||||
log->pop(); // bold
|
||||
tool_button->set_button_icon(icon);
|
||||
} break;
|
||||
case MSG_TYPE_WARNING: {
|
||||
log->push_color(theme_cache.warning_color);
|
||||
Ref<Texture2D> icon = theme_cache.warning_icon;
|
||||
log->add_image(icon);
|
||||
log->add_text(" ");
|
||||
tool_button->set_icon(icon);
|
||||
log->push_bold();
|
||||
log->add_text(" WARNING: ");
|
||||
log->pop(); // bold
|
||||
tool_button->set_button_icon(icon);
|
||||
} break;
|
||||
case MSG_TYPE_EDITOR: {
|
||||
// Distinguish editor messages from messages printed by the project
|
||||
|
|
@ -398,9 +397,7 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
|
|||
if (p_replace_previous) {
|
||||
// Force sync last line update (skip if number of unprocessed log messages is too large to avoid editor lag).
|
||||
if (log->get_pending_paragraphs() < 100) {
|
||||
while (!log->is_ready()) {
|
||||
::OS::get_singleton()->delay_usec(1);
|
||||
}
|
||||
log->wait_until_finished();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -484,17 +481,17 @@ EditorLog::EditorLog() {
|
|||
|
||||
// Clear.
|
||||
clear_button = memnew(Button);
|
||||
clear_button->set_theme_type_variation("FlatButton");
|
||||
clear_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
clear_button->set_focus_mode(FOCUS_NONE);
|
||||
clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTR("Clear Output"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::K));
|
||||
clear_button->set_shortcut(ED_SHORTCUT("editor/clear_output", TTRC("Clear Output"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::K));
|
||||
clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorLog::_clear_request));
|
||||
hb_tools->add_child(clear_button);
|
||||
|
||||
// Copy.
|
||||
copy_button = memnew(Button);
|
||||
copy_button->set_theme_type_variation("FlatButton");
|
||||
copy_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
copy_button->set_focus_mode(FOCUS_NONE);
|
||||
copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTR("Copy Selection"), KeyModifierMask::CMD_OR_CTRL | Key::C));
|
||||
copy_button->set_shortcut(ED_SHORTCUT("editor/copy_output", TTRC("Copy Selection"), KeyModifierMask::CMD_OR_CTRL | Key::C));
|
||||
copy_button->set_shortcut_context(this);
|
||||
copy_button->connect(SceneStringName(pressed), callable_mp(this, &EditorLog::_copy_request));
|
||||
hb_tools->add_child(copy_button);
|
||||
|
|
@ -509,23 +506,23 @@ EditorLog::EditorLog() {
|
|||
|
||||
// Collapse.
|
||||
collapse_button = memnew(Button);
|
||||
collapse_button->set_theme_type_variation("FlatButton");
|
||||
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_toggle_mode(true);
|
||||
collapse_button->set_pressed(false);
|
||||
collapse_button->connect("toggled", callable_mp(this, &EditorLog::_set_collapse));
|
||||
collapse_button->connect(SceneStringName(toggled), callable_mp(this, &EditorLog::_set_collapse));
|
||||
hb_tools2->add_child(collapse_button);
|
||||
|
||||
// Show Search.
|
||||
show_search_button = memnew(Button);
|
||||
show_search_button->set_theme_type_variation("FlatButton");
|
||||
show_search_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
show_search_button->set_focus_mode(FOCUS_NONE);
|
||||
show_search_button->set_toggle_mode(true);
|
||||
show_search_button->set_pressed(true);
|
||||
show_search_button->set_shortcut(ED_SHORTCUT("editor/open_search", TTR("Focus Search/Filter Bar"), KeyModifierMask::CMD_OR_CTRL | Key::F));
|
||||
show_search_button->set_shortcut(ED_SHORTCUT("editor/open_search", TTRC("Focus Search/Filter Bar"), KeyModifierMask::CMD_OR_CTRL | Key::F));
|
||||
show_search_button->set_shortcut_context(this);
|
||||
show_search_button->connect("toggled", callable_mp(this, &EditorLog::_set_search_visible));
|
||||
show_search_button->connect(SceneStringName(toggled), callable_mp(this, &EditorLog::_set_search_visible));
|
||||
hb_tools2->add_child(show_search_button);
|
||||
|
||||
// Message Type Filters.
|
||||
|
|
|
|||
|
|
@ -34,12 +34,8 @@
|
|||
#include "core/os/thread.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/rich_text_label.h"
|
||||
#include "scene/gui/texture_button.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
|
||||
class UndoRedo;
|
||||
|
||||
|
|
@ -98,11 +94,9 @@ private:
|
|||
toggle_button->set_pressed(true);
|
||||
toggle_button->set_text(itos(message_count));
|
||||
toggle_button->set_tooltip_text(TTR(p_tooltip));
|
||||
// Don't tint the icon even when in "pressed" state.
|
||||
toggle_button->add_theme_color_override("icon_color_pressed", Color(1, 1, 1, 1));
|
||||
toggle_button->set_focus_mode(FOCUS_NONE);
|
||||
// When toggled call the callback and pass the MessageType this button is for.
|
||||
toggle_button->connect("toggled", p_toggled_callback.bind(type));
|
||||
toggle_button->connect(SceneStringName(toggled), p_toggled_callback.bind(type));
|
||||
}
|
||||
|
||||
int get_message_count() {
|
||||
|
|
@ -145,11 +139,10 @@ private:
|
|||
Button *show_search_button = nullptr;
|
||||
LineEdit *search_box = nullptr;
|
||||
|
||||
// Reference to the "Output" button on the toolbar so we can update it's icon when
|
||||
// Warnings or Errors are encounetered.
|
||||
// Reference to the "Output" button on the toolbar so we can update its icon when warnings or errors are encountered.
|
||||
Button *tool_button = nullptr;
|
||||
|
||||
bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try trigger a save, which happens during loading).
|
||||
bool is_loading_state = false; // Used to disable saving requests while loading (some signals from buttons will try to trigger a save, which happens during loading).
|
||||
Timer *save_state_timer = nullptr;
|
||||
|
||||
static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type);
|
||||
|
|
|
|||
298
engine/editor/editor_main_screen.cpp
Normal file
298
engine/editor/editor_main_screen.cpp
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/**************************************************************************/
|
||||
/* editor_main_screen.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_main_screen.h"
|
||||
|
||||
#include "core/io/config_file.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
|
||||
void EditorMainScreen::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
if (EDITOR_3D < buttons.size() && buttons[EDITOR_3D]->is_visible()) {
|
||||
// If the 3D editor is enabled, use this as the default.
|
||||
select(EDITOR_3D);
|
||||
return;
|
||||
}
|
||||
|
||||
// Switch to the first main screen plugin that is enabled. Usually this is
|
||||
// 2D, but may be subsequent ones if 2D is disabled in the feature profile.
|
||||
for (int i = 0; i < buttons.size(); i++) {
|
||||
Button *editor_button = buttons[i];
|
||||
if (editor_button->is_visible()) {
|
||||
select(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
select(-1);
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
for (int i = 0; i < buttons.size(); i++) {
|
||||
Button *tb = buttons[i];
|
||||
EditorPlugin *p_editor = editor_table[i];
|
||||
Ref<Texture2D> icon = p_editor->get_plugin_icon();
|
||||
|
||||
if (icon.is_valid()) {
|
||||
tb->set_button_icon(icon);
|
||||
} else if (has_theme_icon(p_editor->get_plugin_name(), EditorStringName(EditorIcons))) {
|
||||
tb->set_button_icon(get_theme_icon(p_editor->get_plugin_name(), EditorStringName(EditorIcons)));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorMainScreen::set_button_container(HBoxContainer *p_button_hb) {
|
||||
button_hb = p_button_hb;
|
||||
}
|
||||
|
||||
void EditorMainScreen::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const {
|
||||
int selected_main_editor_idx = -1;
|
||||
for (int i = 0; i < buttons.size(); i++) {
|
||||
if (buttons[i]->is_pressed()) {
|
||||
selected_main_editor_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (selected_main_editor_idx != -1) {
|
||||
p_config_file->set_value(p_section, "selected_main_editor_idx", selected_main_editor_idx);
|
||||
} else {
|
||||
p_config_file->set_value(p_section, "selected_main_editor_idx", Variant());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorMainScreen::load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section) {
|
||||
int selected_main_editor_idx = p_config_file->get_value(p_section, "selected_main_editor_idx", -1);
|
||||
if (selected_main_editor_idx >= 0 && selected_main_editor_idx < buttons.size()) {
|
||||
callable_mp(this, &EditorMainScreen::select).call_deferred(selected_main_editor_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorMainScreen::set_button_enabled(int p_index, bool p_enabled) {
|
||||
ERR_FAIL_INDEX(p_index, buttons.size());
|
||||
buttons[p_index]->set_visible(p_enabled);
|
||||
if (!p_enabled && buttons[p_index]->is_pressed()) {
|
||||
select(EDITOR_2D);
|
||||
}
|
||||
}
|
||||
|
||||
bool EditorMainScreen::is_button_enabled(int p_index) const {
|
||||
ERR_FAIL_INDEX_V(p_index, buttons.size(), false);
|
||||
return buttons[p_index]->is_visible();
|
||||
}
|
||||
|
||||
int EditorMainScreen::_get_current_main_editor() const {
|
||||
for (int i = 0; i < editor_table.size(); i++) {
|
||||
if (editor_table[i] == selected_plugin) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EditorMainScreen::select_next() {
|
||||
int editor = _get_current_main_editor();
|
||||
|
||||
do {
|
||||
if (editor == editor_table.size() - 1) {
|
||||
editor = 0;
|
||||
} else {
|
||||
editor++;
|
||||
}
|
||||
} while (!buttons[editor]->is_visible());
|
||||
|
||||
select(editor);
|
||||
}
|
||||
|
||||
void EditorMainScreen::select_prev() {
|
||||
int editor = _get_current_main_editor();
|
||||
|
||||
do {
|
||||
if (editor == 0) {
|
||||
editor = editor_table.size() - 1;
|
||||
} else {
|
||||
editor--;
|
||||
}
|
||||
} while (!buttons[editor]->is_visible());
|
||||
|
||||
select(editor);
|
||||
}
|
||||
|
||||
void EditorMainScreen::select_by_name(const String &p_name) {
|
||||
ERR_FAIL_COND(p_name.is_empty());
|
||||
|
||||
for (int i = 0; i < buttons.size(); i++) {
|
||||
if (buttons[i]->get_text() == p_name) {
|
||||
select(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_MSG("The editor name '" + p_name + "' was not found.");
|
||||
}
|
||||
|
||||
void EditorMainScreen::select(int p_index) {
|
||||
if (EditorNode::get_singleton()->is_changing_scene()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX(p_index, editor_table.size());
|
||||
|
||||
if (!buttons[p_index]->is_visible()) { // Button hidden, no editor.
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buttons.size(); i++) {
|
||||
buttons[i]->set_pressed_no_signal(i == p_index);
|
||||
}
|
||||
|
||||
EditorPlugin *new_editor = editor_table[p_index];
|
||||
ERR_FAIL_NULL(new_editor);
|
||||
|
||||
if (selected_plugin == new_editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected_plugin) {
|
||||
selected_plugin->make_visible(false);
|
||||
}
|
||||
|
||||
selected_plugin = new_editor;
|
||||
selected_plugin->make_visible(true);
|
||||
selected_plugin->selected_notify();
|
||||
|
||||
EditorData &editor_data = EditorNode::get_editor_data();
|
||||
int plugin_count = editor_data.get_editor_plugin_count();
|
||||
for (int i = 0; i < plugin_count; i++) {
|
||||
editor_data.get_editor_plugin(i)->notify_main_screen_changed(selected_plugin->get_plugin_name());
|
||||
}
|
||||
|
||||
EditorNode::get_singleton()->update_distraction_free_mode();
|
||||
}
|
||||
|
||||
int EditorMainScreen::get_selected_index() const {
|
||||
for (int i = 0; i < editor_table.size(); i++) {
|
||||
if (selected_plugin == editor_table[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int EditorMainScreen::get_plugin_index(EditorPlugin *p_editor) const {
|
||||
int screen = -1;
|
||||
for (int i = 0; i < editor_table.size(); i++) {
|
||||
if (p_editor == editor_table[i]) {
|
||||
screen = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
|
||||
EditorPlugin *EditorMainScreen::get_selected_plugin() const {
|
||||
return selected_plugin;
|
||||
}
|
||||
|
||||
EditorPlugin *EditorMainScreen::get_plugin_by_name(const String &p_plugin_name) const {
|
||||
ERR_FAIL_COND_V(!main_editor_plugins.has(p_plugin_name), nullptr);
|
||||
return main_editor_plugins[p_plugin_name];
|
||||
}
|
||||
|
||||
VBoxContainer *EditorMainScreen::get_control() const {
|
||||
return main_screen_vbox;
|
||||
}
|
||||
|
||||
void EditorMainScreen::add_main_plugin(EditorPlugin *p_editor) {
|
||||
Button *tb = memnew(Button);
|
||||
tb->set_toggle_mode(true);
|
||||
tb->set_theme_type_variation("MainScreenButton");
|
||||
tb->set_name(p_editor->get_plugin_name());
|
||||
tb->set_text(p_editor->get_plugin_name());
|
||||
|
||||
Ref<Texture2D> icon = p_editor->get_plugin_icon();
|
||||
if (icon.is_null() && has_theme_icon(p_editor->get_plugin_name(), EditorStringName(EditorIcons))) {
|
||||
icon = get_editor_theme_icon(p_editor->get_plugin_name());
|
||||
}
|
||||
if (icon.is_valid()) {
|
||||
tb->set_button_icon(icon);
|
||||
// Make sure the control is updated if the icon is reimported.
|
||||
icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size));
|
||||
}
|
||||
|
||||
tb->connect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select).bind(buttons.size()));
|
||||
|
||||
buttons.push_back(tb);
|
||||
button_hb->add_child(tb);
|
||||
editor_table.push_back(p_editor);
|
||||
main_editor_plugins.insert(p_editor->get_plugin_name(), p_editor);
|
||||
}
|
||||
|
||||
void EditorMainScreen::remove_main_plugin(EditorPlugin *p_editor) {
|
||||
// Remove the main editor button and update the bindings of
|
||||
// all buttons behind it to point to the correct main window.
|
||||
for (int i = buttons.size() - 1; i >= 0; i--) {
|
||||
if (p_editor->get_plugin_name() == buttons[i]->get_text()) {
|
||||
if (buttons[i]->is_pressed()) {
|
||||
select(EDITOR_SCRIPT);
|
||||
}
|
||||
|
||||
memdelete(buttons[i]);
|
||||
buttons.remove_at(i);
|
||||
|
||||
break;
|
||||
} else {
|
||||
buttons[i]->disconnect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select));
|
||||
buttons[i]->connect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select).bind(i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (selected_plugin == p_editor) {
|
||||
selected_plugin = nullptr;
|
||||
}
|
||||
|
||||
editor_table.erase(p_editor);
|
||||
main_editor_plugins.erase(p_editor->get_plugin_name());
|
||||
}
|
||||
|
||||
EditorMainScreen::EditorMainScreen() {
|
||||
main_screen_vbox = memnew(VBoxContainer);
|
||||
main_screen_vbox->set_name("MainScreen");
|
||||
main_screen_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
main_screen_vbox->add_theme_constant_override("separation", 0);
|
||||
add_child(main_screen_vbox);
|
||||
}
|
||||
94
engine/editor/editor_main_screen.h
Normal file
94
engine/editor/editor_main_screen.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/**************************************************************************/
|
||||
/* editor_main_screen.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef EDITOR_MAIN_SCREEN_H
|
||||
#define EDITOR_MAIN_SCREEN_H
|
||||
|
||||
#include "scene/gui/panel_container.h"
|
||||
|
||||
class Button;
|
||||
class ConfigFile;
|
||||
class EditorPlugin;
|
||||
class HBoxContainer;
|
||||
class VBoxContainer;
|
||||
|
||||
class EditorMainScreen : public PanelContainer {
|
||||
GDCLASS(EditorMainScreen, PanelContainer);
|
||||
|
||||
public:
|
||||
enum EditorTable {
|
||||
EDITOR_2D = 0,
|
||||
EDITOR_3D,
|
||||
EDITOR_SCRIPT,
|
||||
EDITOR_GAME,
|
||||
EDITOR_ASSETLIB,
|
||||
};
|
||||
|
||||
private:
|
||||
VBoxContainer *main_screen_vbox = nullptr;
|
||||
EditorPlugin *selected_plugin = nullptr;
|
||||
|
||||
HBoxContainer *button_hb = nullptr;
|
||||
Vector<Button *> buttons;
|
||||
Vector<EditorPlugin *> editor_table;
|
||||
HashMap<String, EditorPlugin *> main_editor_plugins;
|
||||
|
||||
int _get_current_main_editor() const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void set_button_container(HBoxContainer *p_button_hb);
|
||||
|
||||
void save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const;
|
||||
void load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section);
|
||||
|
||||
void set_button_enabled(int p_index, bool p_enabled);
|
||||
bool is_button_enabled(int p_index) const;
|
||||
|
||||
void select_next();
|
||||
void select_prev();
|
||||
void select_by_name(const String &p_name);
|
||||
void select(int p_index);
|
||||
int get_selected_index() const;
|
||||
int get_plugin_index(EditorPlugin *p_editor) const;
|
||||
EditorPlugin *get_selected_plugin() const;
|
||||
EditorPlugin *get_plugin_by_name(const String &p_plugin_name) const;
|
||||
|
||||
VBoxContainer *get_control() const;
|
||||
|
||||
void add_main_plugin(EditorPlugin *p_editor);
|
||||
void remove_main_plugin(EditorPlugin *p_editor);
|
||||
|
||||
EditorMainScreen();
|
||||
};
|
||||
|
||||
#endif // EDITOR_MAIN_SCREEN_H
|
||||
|
|
@ -30,10 +30,10 @@
|
|||
|
||||
#include "editor_native_shader_source_visualizer.h"
|
||||
|
||||
#include "editor/code_editor.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/code_edit.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
#include "servers/rendering/shader_language.h"
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ void EditorNativeShaderSourceVisualizer::_inspect_shader(RID p_shader) {
|
|||
code_edit->set_syntax_highlighter(syntax_highlighter);
|
||||
code_edit->add_theme_font_override(SceneStringName(font), get_theme_font("source", EditorStringName(EditorFonts)));
|
||||
code_edit->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size("source_size", EditorStringName(EditorFonts)));
|
||||
code_edit->add_theme_constant_override("line_spacing", EDITOR_DEF("text_editor/theme/line_spacing", 6));
|
||||
code_edit->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/theme/line_spacing"));
|
||||
|
||||
// Appearance: Caret
|
||||
code_edit->set_caret_type((TextEdit::CaretType)EDITOR_GET("text_editor/appearance/caret/type").operator int());
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -42,27 +42,17 @@ typedef void (*EditorPluginInitializeCallback)();
|
|||
typedef bool (*EditorBuildCallback)();
|
||||
|
||||
class AcceptDialog;
|
||||
class CenterContainer;
|
||||
class CheckBox;
|
||||
class ColorPicker;
|
||||
class ConfirmationDialog;
|
||||
class Control;
|
||||
class FileDialog;
|
||||
class HBoxContainer;
|
||||
class HSplitContainer;
|
||||
class LinkButton;
|
||||
class MenuBar;
|
||||
class MenuButton;
|
||||
class Node2D;
|
||||
class OptionButton;
|
||||
class Panel;
|
||||
class PanelContainer;
|
||||
class PopupPanel;
|
||||
class RichTextLabel;
|
||||
class SubViewport;
|
||||
class TabBar;
|
||||
class TabContainer;
|
||||
class TextureRect;
|
||||
class TextureProgressBar;
|
||||
class Tree;
|
||||
class VBoxContainer;
|
||||
|
|
@ -83,56 +73,49 @@ class EditorCommandPalette;
|
|||
class EditorDockManager;
|
||||
class EditorExport;
|
||||
class EditorExportPreset;
|
||||
class EditorExtensionManager;
|
||||
class EditorFeatureProfileManager;
|
||||
class EditorFileDialog;
|
||||
class EditorFolding;
|
||||
class EditorInspector;
|
||||
class EditorLayoutsDialog;
|
||||
class EditorLog;
|
||||
class EditorMainScreen;
|
||||
class EditorNativeShaderSourceVisualizer;
|
||||
class EditorPluginList;
|
||||
class EditorQuickOpen;
|
||||
class EditorPropertyResource;
|
||||
class EditorResourcePreview;
|
||||
class EditorResourceConversionPlugin;
|
||||
class EditorRunBar;
|
||||
class EditorRunNative;
|
||||
class EditorSceneTabs;
|
||||
class EditorSelectionHistory;
|
||||
class EditorSettingsDialog;
|
||||
class EditorTitleBar;
|
||||
class EditorToaster;
|
||||
class EditorUndoRedoManager;
|
||||
class ExportTemplateManager;
|
||||
class EditorQuickOpenDialog;
|
||||
class FBXImporterManager;
|
||||
class FileSystemDock;
|
||||
class HistoryDock;
|
||||
class ImportDock;
|
||||
class NodeDock;
|
||||
class OrphanResourcesDialog;
|
||||
class PluginConfigDialog;
|
||||
class ProgressDialog;
|
||||
class ProjectExportDialog;
|
||||
class ProjectSettingsEditor;
|
||||
class RunSettingsDialog;
|
||||
class SceneImportSettingsDialog;
|
||||
class ScriptCreateDialog;
|
||||
class SurfaceUpgradeTool;
|
||||
class SurfaceUpgradeDialog;
|
||||
class WindowWrapper;
|
||||
class UIDUpgradeTool;
|
||||
class UIDUpgradeDialog;
|
||||
|
||||
struct EditorProgress {
|
||||
String task;
|
||||
bool force_background = false;
|
||||
bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true);
|
||||
|
||||
EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false, bool p_force_background = false);
|
||||
~EditorProgress();
|
||||
};
|
||||
|
||||
class EditorNode : public Node {
|
||||
GDCLASS(EditorNode, Node);
|
||||
|
||||
public:
|
||||
enum EditorTable {
|
||||
EDITOR_2D = 0,
|
||||
EDITOR_3D,
|
||||
EDITOR_SCRIPT,
|
||||
EDITOR_ASSETLIB
|
||||
};
|
||||
|
||||
enum SceneNameCasing {
|
||||
SCENE_NAME_CASING_AUTO,
|
||||
SCENE_NAME_CASING_PASCAL_CASE,
|
||||
|
|
@ -151,6 +134,87 @@ public:
|
|||
ACTION_ON_STOP_CLOSE_BUTTOM_PANEL,
|
||||
};
|
||||
|
||||
enum MenuOptions {
|
||||
// Scene menu.
|
||||
FILE_NEW_SCENE,
|
||||
FILE_NEW_INHERITED_SCENE,
|
||||
FILE_OPEN_SCENE,
|
||||
FILE_OPEN_PREV,
|
||||
FILE_OPEN_RECENT,
|
||||
FILE_SAVE_SCENE,
|
||||
FILE_SAVE_AS_SCENE,
|
||||
FILE_SAVE_ALL_SCENES,
|
||||
FILE_QUICK_OPEN,
|
||||
FILE_QUICK_OPEN_SCENE,
|
||||
FILE_QUICK_OPEN_SCRIPT,
|
||||
FILE_UNDO,
|
||||
FILE_REDO,
|
||||
FILE_RELOAD_SAVED_SCENE,
|
||||
FILE_CLOSE,
|
||||
FILE_QUIT,
|
||||
|
||||
FILE_EXPORT_MESH_LIBRARY,
|
||||
|
||||
// Project menu.
|
||||
PROJECT_OPEN_SETTINGS,
|
||||
PROJECT_VERSION_CONTROL,
|
||||
PROJECT_EXPORT,
|
||||
PROJECT_PACK_AS_ZIP,
|
||||
PROJECT_INSTALL_ANDROID_SOURCE,
|
||||
PROJECT_OPEN_USER_DATA_FOLDER,
|
||||
PROJECT_RELOAD_CURRENT_PROJECT,
|
||||
PROJECT_QUIT_TO_PROJECT_MANAGER,
|
||||
|
||||
TOOLS_ORPHAN_RESOURCES,
|
||||
TOOLS_BUILD_PROFILE_MANAGER,
|
||||
TOOLS_SURFACE_UPGRADE,
|
||||
TOOLS_UID_UPGRADE,
|
||||
TOOLS_CUSTOM,
|
||||
|
||||
VCS_METADATA,
|
||||
VCS_SETTINGS,
|
||||
|
||||
// Editor menu.
|
||||
EDITOR_OPEN_SETTINGS,
|
||||
EDITOR_COMMAND_PALETTE,
|
||||
EDITOR_TAKE_SCREENSHOT,
|
||||
EDITOR_TOGGLE_FULLSCREEN,
|
||||
EDITOR_OPEN_DATA_FOLDER,
|
||||
EDITOR_OPEN_CONFIG_FOLDER,
|
||||
EDITOR_MANAGE_FEATURE_PROFILES,
|
||||
EDITOR_MANAGE_EXPORT_TEMPLATES,
|
||||
EDITOR_CONFIGURE_FBX_IMPORTER,
|
||||
|
||||
LAYOUT_SAVE,
|
||||
LAYOUT_DELETE,
|
||||
LAYOUT_DEFAULT,
|
||||
|
||||
// Help menu.
|
||||
HELP_SEARCH,
|
||||
HELP_DOCS,
|
||||
HELP_FORUM,
|
||||
HELP_COMMUNITY,
|
||||
HELP_COPY_SYSTEM_INFO,
|
||||
HELP_REPORT_A_BUG,
|
||||
HELP_SUGGEST_A_FEATURE,
|
||||
HELP_SEND_DOCS_FEEDBACK,
|
||||
HELP_ABOUT,
|
||||
HELP_SUPPORT_GODOT_DEVELOPMENT,
|
||||
|
||||
// Update spinner menu.
|
||||
SPINNER_UPDATE_CONTINUOUSLY,
|
||||
SPINNER_UPDATE_WHEN_CHANGED,
|
||||
SPINNER_UPDATE_SPINNER_HIDE,
|
||||
|
||||
// Non-menu options.
|
||||
SCENE_TAB_CLOSE,
|
||||
FILE_SAVE_AND_RUN,
|
||||
FILE_SAVE_AND_RUN_MAIN_SCENE,
|
||||
RESOURCE_SAVE,
|
||||
RESOURCE_SAVE_AS,
|
||||
SETTINGS_PICK_MAIN_SCENE,
|
||||
};
|
||||
|
||||
struct ExecuteThreadArgs {
|
||||
String path;
|
||||
List<String> args;
|
||||
|
|
@ -165,98 +229,9 @@ private:
|
|||
friend class EditorSceneTabs;
|
||||
friend class SurfaceUpgradeTool;
|
||||
|
||||
enum MenuOptions {
|
||||
FILE_NEW_SCENE,
|
||||
FILE_NEW_INHERITED_SCENE,
|
||||
FILE_OPEN_SCENE,
|
||||
FILE_SAVE_SCENE,
|
||||
FILE_SAVE_SCENE_SILENTLY,
|
||||
FILE_SAVE_AS_SCENE,
|
||||
FILE_SAVE_ALL_SCENES,
|
||||
FILE_SAVE_AND_RUN,
|
||||
FILE_SAVE_AND_RUN_MAIN_SCENE,
|
||||
FILE_RUN_SCENE,
|
||||
FILE_SHOW_IN_FILESYSTEM,
|
||||
FILE_EXPORT_PROJECT,
|
||||
FILE_EXPORT_MESH_LIBRARY,
|
||||
FILE_INSTALL_ANDROID_SOURCE,
|
||||
FILE_EXPLORE_ANDROID_BUILD_TEMPLATES,
|
||||
FILE_SAVE_OPTIMIZED,
|
||||
FILE_OPEN_RECENT,
|
||||
FILE_OPEN_OLD_SCENE,
|
||||
FILE_QUICK_OPEN,
|
||||
FILE_QUICK_OPEN_SCENE,
|
||||
FILE_QUICK_OPEN_SCRIPT,
|
||||
FILE_OPEN_PREV,
|
||||
FILE_CLOSE,
|
||||
FILE_CLOSE_OTHERS,
|
||||
FILE_CLOSE_RIGHT,
|
||||
FILE_CLOSE_ALL,
|
||||
FILE_QUIT,
|
||||
FILE_EXTERNAL_OPEN_SCENE,
|
||||
EDIT_UNDO,
|
||||
EDIT_REDO,
|
||||
EDIT_RELOAD_SAVED_SCENE,
|
||||
TOOLS_ORPHAN_RESOURCES,
|
||||
TOOLS_BUILD_PROFILE_MANAGER,
|
||||
TOOLS_SURFACE_UPGRADE,
|
||||
TOOLS_CUSTOM,
|
||||
RESOURCE_SAVE,
|
||||
RESOURCE_SAVE_AS,
|
||||
|
||||
RUN_SETTINGS,
|
||||
RUN_USER_DATA_FOLDER,
|
||||
RELOAD_CURRENT_PROJECT,
|
||||
RUN_PROJECT_MANAGER,
|
||||
VCS_MENU,
|
||||
RUN_VCS_METADATA,
|
||||
RUN_VCS_SETTINGS,
|
||||
SETTINGS_UPDATE_CONTINUOUSLY,
|
||||
SETTINGS_UPDATE_WHEN_CHANGED,
|
||||
SETTINGS_UPDATE_ALWAYS,
|
||||
SETTINGS_UPDATE_CHANGES,
|
||||
SETTINGS_UPDATE_SPINNER_HIDE,
|
||||
SETTINGS_PREFERENCES,
|
||||
SETTINGS_LAYOUT_SAVE,
|
||||
SETTINGS_LAYOUT_DELETE,
|
||||
SETTINGS_LAYOUT_DEFAULT,
|
||||
SETTINGS_EDITOR_DATA_FOLDER,
|
||||
SETTINGS_EDITOR_CONFIG_FOLDER,
|
||||
SETTINGS_MANAGE_EXPORT_TEMPLATES,
|
||||
SETTINGS_MANAGE_FBX_IMPORTER,
|
||||
SETTINGS_MANAGE_FEATURE_PROFILES,
|
||||
SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE,
|
||||
SETTINGS_PICK_MAIN_SCENE,
|
||||
SETTINGS_TOGGLE_FULLSCREEN,
|
||||
SETTINGS_HELP,
|
||||
|
||||
SCENE_TAB_CLOSE,
|
||||
|
||||
EDITOR_SCREENSHOT,
|
||||
EDITOR_OPEN_SCREENSHOT,
|
||||
|
||||
HELP_SEARCH,
|
||||
HELP_COMMAND_PALETTE,
|
||||
HELP_DOCS,
|
||||
HELP_FORUM,
|
||||
HELP_REPORT_A_BUG,
|
||||
HELP_COPY_SYSTEM_INFO,
|
||||
HELP_SUGGEST_A_FEATURE,
|
||||
HELP_SEND_DOCS_FEEDBACK,
|
||||
HELP_COMMUNITY,
|
||||
HELP_ABOUT,
|
||||
HELP_SUPPORT_GODOT_DEVELOPMENT,
|
||||
|
||||
SET_RENDERER_NAME_SAVE_AND_RESTART,
|
||||
|
||||
IMPORT_PLUGIN_BASE = 100,
|
||||
|
||||
TOOL_MENU_BASE = 1000
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX_INIT_CALLBACKS = 128,
|
||||
MAX_BUILD_CALLBACKS = 128
|
||||
MAX_BUILD_CALLBACKS = 128,
|
||||
};
|
||||
|
||||
struct ExportDefer {
|
||||
|
|
@ -265,6 +240,8 @@ private:
|
|||
bool debug = false;
|
||||
bool pack_only = false;
|
||||
bool android_build_template = false;
|
||||
bool patch = false;
|
||||
Vector<String> patches;
|
||||
} export_defer;
|
||||
|
||||
static EditorNode *singleton;
|
||||
|
|
@ -274,14 +251,14 @@ private:
|
|||
EditorSelectionHistory editor_history;
|
||||
|
||||
EditorCommandPalette *command_palette = nullptr;
|
||||
EditorQuickOpenDialog *quick_open_dialog = nullptr;
|
||||
EditorExport *editor_export = nullptr;
|
||||
EditorLog *log = nullptr;
|
||||
EditorNativeShaderSourceVisualizer *native_shader_source_visualizer = nullptr;
|
||||
EditorPlugin *editor_plugin_screen = nullptr;
|
||||
EditorPluginList *editor_plugins_force_input_forwarding = nullptr;
|
||||
EditorPluginList *editor_plugins_force_over = nullptr;
|
||||
EditorPluginList *editor_plugins_over = nullptr;
|
||||
EditorQuickOpen *quick_open = nullptr;
|
||||
EditorQuickOpenDialog *quick_open_color_palette = nullptr;
|
||||
EditorResourcePreview *resource_preview = nullptr;
|
||||
EditorSelection *editor_selection = nullptr;
|
||||
EditorSettingsDialog *editor_settings_dialog = nullptr;
|
||||
|
|
@ -299,7 +276,6 @@ private:
|
|||
HashMap<ObjectID, HashSet<EditorPlugin *>> active_plugins;
|
||||
bool is_main_screen_editing = false;
|
||||
|
||||
PanelContainer *scene_root_parent = nullptr;
|
||||
Control *gui_base = nullptr;
|
||||
VBoxContainer *main_vbox = nullptr;
|
||||
OptionButton *renderer = nullptr;
|
||||
|
|
@ -340,7 +316,6 @@ private:
|
|||
Control *right_menu_spacer = nullptr;
|
||||
EditorTitleBar *title_bar = nullptr;
|
||||
EditorRunBar *project_run_bar = nullptr;
|
||||
VBoxContainer *main_screen_vbox = nullptr;
|
||||
MenuBar *main_menu = nullptr;
|
||||
PopupMenu *apple_menu = nullptr;
|
||||
PopupMenu *file_menu = nullptr;
|
||||
|
|
@ -360,6 +335,7 @@ private:
|
|||
|
||||
RichTextLabel *load_errors = nullptr;
|
||||
AcceptDialog *load_error_dialog = nullptr;
|
||||
bool load_errors_queued_to_display = false;
|
||||
|
||||
RichTextLabel *execute_outputs = nullptr;
|
||||
AcceptDialog *execute_output_dialog = nullptr;
|
||||
|
|
@ -414,9 +390,7 @@ private:
|
|||
String current_path;
|
||||
MenuButton *update_spinner = nullptr;
|
||||
|
||||
HBoxContainer *main_editor_button_hb = nullptr;
|
||||
Vector<Button *> main_editor_buttons;
|
||||
Vector<EditorPlugin *> editor_table;
|
||||
EditorMainScreen *editor_main_screen = nullptr;
|
||||
|
||||
AudioStreamPreviewGenerator *audio_preview_gen = nullptr;
|
||||
ProgressDialog *progress_dialog = nullptr;
|
||||
|
|
@ -434,6 +408,7 @@ private:
|
|||
Timer *editor_layout_save_delay_timer = nullptr;
|
||||
Timer *scan_changes_timer = nullptr;
|
||||
Button *distraction_free = nullptr;
|
||||
Callable palette_file_selected_callback;
|
||||
|
||||
EditorBottomPanel *bottom_panel = nullptr;
|
||||
|
||||
|
|
@ -453,6 +428,7 @@ private:
|
|||
|
||||
bool requested_first_scan = false;
|
||||
bool waiting_for_first_scan = true;
|
||||
bool load_editor_layout_done = false;
|
||||
|
||||
int current_menu_option = 0;
|
||||
|
||||
|
|
@ -466,10 +442,8 @@ private:
|
|||
uint64_t update_spinner_step_frame = 0;
|
||||
int update_spinner_step = 0;
|
||||
|
||||
String _tmp_import_path;
|
||||
String external_file;
|
||||
String open_navigate;
|
||||
String saving_scene;
|
||||
EditorProgress *save_scene_progress = nullptr;
|
||||
|
||||
DynamicFontImportSettingsDialog *fontdata_import_settings = nullptr;
|
||||
SceneImportSettingsDialog *scene_import_settings = nullptr;
|
||||
|
|
@ -478,6 +452,7 @@ private:
|
|||
String import_reload_fn;
|
||||
|
||||
HashSet<String> textfile_extensions;
|
||||
HashSet<String> other_file_extensions;
|
||||
HashSet<FileDialog *> file_dialogs;
|
||||
HashSet<EditorFileDialog *> editor_file_dialogs;
|
||||
|
||||
|
|
@ -488,8 +463,19 @@ private:
|
|||
|
||||
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;
|
||||
|
||||
bool was_window_windowed_last = false;
|
||||
|
||||
bool unfocused_low_processor_usage_mode_enabled = true;
|
||||
|
||||
static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
|
||||
static EditorPluginInitializeCallback plugin_init_callbacks[MAX_INIT_CALLBACKS];
|
||||
static int build_callback_count;
|
||||
|
|
@ -539,6 +525,8 @@ private:
|
|||
|
||||
void _android_build_source_selected(const String &p_file);
|
||||
void _android_export_preset_selected(int p_index);
|
||||
void _android_install_build_template();
|
||||
void _android_explore_build_templates();
|
||||
|
||||
void _request_screenshot();
|
||||
void _screenshot(bool p_use_utc = false);
|
||||
|
|
@ -550,6 +538,7 @@ private:
|
|||
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);
|
||||
void _plugin_over_edit(EditorPlugin *p_plugin, Object *p_object);
|
||||
|
|
@ -559,10 +548,9 @@ private:
|
|||
void _resources_reimporting(const Vector<String> &p_resources);
|
||||
void _resources_reimported(const Vector<String> &p_resources);
|
||||
void _sources_changed(bool p_exist);
|
||||
void _remove_lock_file();
|
||||
|
||||
void _node_renamed();
|
||||
void _editor_select_next();
|
||||
void _editor_select_prev();
|
||||
void _save_editor_states(const String &p_file, int p_idx = -1);
|
||||
void _load_editor_plugin_states_from_config(const Ref<ConfigFile> &p_config_file);
|
||||
void _update_title();
|
||||
|
|
@ -571,10 +559,12 @@ private:
|
|||
void _show_messages();
|
||||
void _vp_resized();
|
||||
void _titlebar_resized();
|
||||
void _viewport_resized();
|
||||
|
||||
void _update_undo_redo_allowed();
|
||||
|
||||
int _save_external_resources(bool p_also_save_external_data = false);
|
||||
void _save_scene_silently();
|
||||
|
||||
void _set_current_scene(int p_idx);
|
||||
void _set_current_scene_nocheck(int p_idx);
|
||||
|
|
@ -586,10 +576,12 @@ private:
|
|||
void _scene_tab_closed(int p_tab);
|
||||
void _cancel_close_scene_tab();
|
||||
|
||||
void _prepare_save_confirmation_popup();
|
||||
|
||||
void _inherit_request(String p_file);
|
||||
void _instantiate_request(const Vector<String> &p_files);
|
||||
|
||||
void _quick_opened();
|
||||
void _quick_opened(const String &p_file_path);
|
||||
void _open_command_palette();
|
||||
|
||||
void _project_run_started();
|
||||
|
|
@ -608,9 +600,11 @@ private:
|
|||
void _renderer_selected(int);
|
||||
void _update_renderer_color();
|
||||
void _add_renderer_entry(const String &p_renderer_name, bool p_mark_overridden);
|
||||
void _set_renderer_name_save_and_restart();
|
||||
|
||||
void _exit_editor(int p_exit_code);
|
||||
|
||||
virtual void input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
bool has_main_screen() const { return true; }
|
||||
|
|
@ -624,23 +618,25 @@ private:
|
|||
|
||||
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);
|
||||
void _close_save_scene_progress();
|
||||
|
||||
bool _find_scene_in_use(Node *p_node, const String &p_path) const;
|
||||
|
||||
void _proceed_closing_scene_tabs();
|
||||
bool _is_closing_editor() const;
|
||||
void _restart_editor(bool p_goto_project_manager = false);
|
||||
|
||||
Dictionary _get_main_scene_state();
|
||||
void _set_main_scene_state(Dictionary p_state, Node *p_for_scene);
|
||||
|
||||
int _get_current_main_editor();
|
||||
|
||||
void _save_editor_layout();
|
||||
void _load_editor_layout();
|
||||
|
||||
void _save_central_editor_layout_to_config(Ref<ConfigFile> p_config_file);
|
||||
void _load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file);
|
||||
|
||||
void _save_window_settings_to_config(Ref<ConfigFile> p_layout, const String &p_section);
|
||||
|
||||
void _save_open_scenes_to_config(Ref<ConfigFile> p_layout);
|
||||
void _load_open_scenes_from_config(Ref<ConfigFile> p_layout);
|
||||
|
||||
|
|
@ -665,12 +661,11 @@ private:
|
|||
void _feature_profile_changed();
|
||||
bool _is_class_editor_disabled_by_feature_profile(const StringName &p_class);
|
||||
|
||||
Ref<Texture2D> _get_class_or_script_icon(const String &p_class, const Ref<Script> &p_script, const String &p_fallback = "Object", bool p_fallback_script_to_theme = false);
|
||||
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);
|
||||
|
||||
void _pick_main_scene_custom_action(const String &p_custom_action_name);
|
||||
|
||||
void _immediate_dialog_confirmed();
|
||||
void _select_default_main_screen_plugin();
|
||||
|
||||
void _begin_first_scan();
|
||||
|
||||
|
|
@ -678,6 +673,11 @@ private:
|
|||
|
||||
void _remove_all_not_owned_children(Node *p_node, Node *p_owner);
|
||||
|
||||
void _progress_dialog_visibility_changed();
|
||||
void _load_error_dialog_visibility_changed();
|
||||
|
||||
void _execute_upgrades();
|
||||
|
||||
protected:
|
||||
friend class FileSystemDock;
|
||||
|
||||
|
|
@ -689,9 +689,6 @@ public:
|
|||
void init_plugins();
|
||||
void _on_plugin_ready(Object *p_script, const String &p_activate_name);
|
||||
|
||||
void editor_select(int p_which);
|
||||
void set_visible_editor(EditorTable p_table) { editor_select(p_table); }
|
||||
|
||||
bool call_build();
|
||||
|
||||
// This is a very naive estimation, but we need something now. Will be reworked later.
|
||||
|
|
@ -706,6 +703,7 @@ public:
|
|||
static EditorTitleBar *get_title_bar() { return singleton->title_bar; }
|
||||
static VSplitContainer *get_top_split() { return singleton->top_split; }
|
||||
static EditorBottomPanel *get_bottom_panel() { return singleton->bottom_panel; }
|
||||
static EditorMainScreen *get_editor_main_screen() { return singleton->editor_main_screen; }
|
||||
|
||||
static String adjust_scene_name_casing(const String &p_root_name);
|
||||
static String adjust_script_name_casing(const String &p_file_name, ScriptLanguage::ScriptNameCasing p_auto_casing);
|
||||
|
|
@ -737,7 +735,6 @@ public:
|
|||
|
||||
static void cleanup();
|
||||
|
||||
EditorPlugin *get_editor_plugin_screen() { return editor_plugin_screen; }
|
||||
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
|
||||
EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; }
|
||||
EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; }
|
||||
|
|
@ -751,6 +748,7 @@ public:
|
|||
|
||||
void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); }
|
||||
|
||||
void update_distraction_free_mode();
|
||||
void set_distraction_free_mode(bool p_enter);
|
||||
bool is_distraction_free_mode_enabled() const;
|
||||
|
||||
|
|
@ -773,9 +771,14 @@ public:
|
|||
void push_node_item(Node *p_node);
|
||||
void hide_unused_editors(const Object *p_editing_owner = nullptr);
|
||||
|
||||
void select_editor_by_name(const String &p_name);
|
||||
|
||||
void open_request(const String &p_path);
|
||||
void replace_resources_in_object(
|
||||
Object *p_object,
|
||||
const Vector<Ref<Resource>> &p_source_resources,
|
||||
const Vector<Ref<Resource>> &p_target_resource);
|
||||
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);
|
||||
|
|
@ -784,7 +787,6 @@ public:
|
|||
|
||||
bool is_changing_scene() const;
|
||||
|
||||
VBoxContainer *get_main_screen_control();
|
||||
SubViewport *get_scene_root() { return scene_root; } // Root of the scene being edited.
|
||||
|
||||
void set_edited_scene(Node *p_scene);
|
||||
|
|
@ -793,15 +795,17 @@ public:
|
|||
|
||||
void fix_dependencies(const String &p_for_file);
|
||||
int new_scene();
|
||||
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
void set_unfocused_low_processor_usage_mode_enabled(bool p_enabled);
|
||||
|
||||
struct AdditiveNodeEntry {
|
||||
Node *node = nullptr;
|
||||
NodePath parent = NodePath();
|
||||
NodePath parent;
|
||||
Node *owner = nullptr;
|
||||
int index = 0;
|
||||
// Used if the original parent node is lost
|
||||
|
|
@ -834,10 +838,19 @@ public:
|
|||
HashMap<NodePath, ModificationNodeEntry> other_instances_modifications;
|
||||
};
|
||||
|
||||
struct SceneEditorDataEntry {
|
||||
bool is_editable = false;
|
||||
bool is_display_folded = false;
|
||||
};
|
||||
|
||||
HashMap<int, SceneModificationsEntry> scenes_modification_table;
|
||||
List<String> scenes_reimported;
|
||||
List<String> resources_reimported;
|
||||
|
||||
void update_node_from_node_modification_entry(Node *p_node, ModificationNodeEntry &p_node_modification);
|
||||
|
||||
void get_scene_editor_data_for_node(Node *p_root, Node *p_node, HashMap<NodePath, SceneEditorDataEntry> &p_table);
|
||||
|
||||
void get_preload_scene_modification_table(
|
||||
Node *p_edited_scene,
|
||||
Node *p_reimported_root,
|
||||
|
|
@ -846,7 +859,7 @@ public:
|
|||
void get_preload_modifications_reference_to_nodes(
|
||||
Node *p_root,
|
||||
Node *p_node,
|
||||
List<Node *> &p_excluded_nodes,
|
||||
HashSet<Node *> &p_excluded_nodes,
|
||||
List<Node *> &p_instance_list_with_children,
|
||||
HashMap<NodePath, ModificationNodeEntry> &p_modification_table);
|
||||
void get_children_nodes(Node *p_node, List<Node *> &p_nodes);
|
||||
|
|
@ -885,7 +898,7 @@ public:
|
|||
|
||||
void _copy_warning(const String &p_str);
|
||||
|
||||
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template);
|
||||
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only, bool p_android_build_template, bool p_patch, const Vector<String> &p_patches);
|
||||
bool is_project_exporting() const;
|
||||
|
||||
Control *get_gui_base() { return gui_base; }
|
||||
|
|
@ -907,7 +920,7 @@ public:
|
|||
|
||||
void reload_scene(const String &p_path);
|
||||
|
||||
void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list);
|
||||
void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, HashSet<Node *> &p_instance_list);
|
||||
void preload_reimporting_with_path_in_edited_scenes(const List<String> &p_scenes);
|
||||
void reload_instances_with_path_in_edited_scenes();
|
||||
|
||||
|
|
@ -916,6 +929,8 @@ public:
|
|||
Dictionary drag_resource(const Ref<Resource> &p_res, Control *p_from);
|
||||
Dictionary drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from);
|
||||
|
||||
EditorQuickOpenDialog *get_quick_open_dialog() { return quick_open_dialog; }
|
||||
|
||||
void add_tool_menu_item(const String &p_name, const Callable &p_callback);
|
||||
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);
|
||||
void remove_tool_menu_item(const String &p_name);
|
||||
|
|
@ -927,13 +942,13 @@ public:
|
|||
void save_scene_list(const HashSet<String> &p_scene_paths);
|
||||
void save_before_run();
|
||||
void try_autosave();
|
||||
void restart_editor();
|
||||
void restart_editor(bool p_goto_project_manager = false);
|
||||
void unload_editor_addons();
|
||||
|
||||
void dim_editor(bool p_dimming);
|
||||
bool is_editor_dimmed() const;
|
||||
|
||||
void edit_current() { _edit_current(); };
|
||||
void edit_current() { _edit_current(); }
|
||||
|
||||
bool has_scenes_in_session();
|
||||
|
||||
|
|
@ -947,38 +962,12 @@ public:
|
|||
|
||||
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
||||
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
|
||||
Vector<Ref<EditorResourceConversionPlugin>> find_resource_conversion_plugin(const Ref<Resource> &p_for_resource);
|
||||
Vector<Ref<EditorResourceConversionPlugin>> find_resource_conversion_plugin_for_resource(const Ref<Resource> &p_for_resource);
|
||||
Vector<Ref<EditorResourceConversionPlugin>> find_resource_conversion_plugin_for_type_name(const String &p_type);
|
||||
|
||||
bool ensure_main_scene(bool p_from_native);
|
||||
};
|
||||
|
||||
struct EditorProgress {
|
||||
String task;
|
||||
bool step(const String &p_state, int p_step = -1, bool p_force_refresh = true) {
|
||||
if (Thread::is_main_thread()) {
|
||||
return EditorNode::progress_task_step(task, p_state, p_step, p_force_refresh);
|
||||
} else {
|
||||
EditorNode::progress_task_step_bg(task, p_step);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EditorProgress(const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false) {
|
||||
if (Thread::is_main_thread()) {
|
||||
EditorNode::progress_add_task(p_task, p_label, p_amount, p_can_cancel);
|
||||
} else {
|
||||
EditorNode::progress_add_task_bg(p_task, p_label, p_amount);
|
||||
}
|
||||
task = p_task;
|
||||
}
|
||||
~EditorProgress() {
|
||||
if (Thread::is_main_thread()) {
|
||||
EditorNode::progress_end_task(task);
|
||||
} else {
|
||||
EditorNode::progress_end_task_bg(task);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class EditorPluginList : public Object {
|
||||
private:
|
||||
Vector<EditorPlugin *> plugins_list;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ String EditorPaths::get_cache_dir() const {
|
|||
return cache_dir;
|
||||
}
|
||||
|
||||
String EditorPaths::get_temp_dir() const {
|
||||
return temp_dir;
|
||||
}
|
||||
|
||||
String EditorPaths::get_project_data_dir() const {
|
||||
return project_data_dir;
|
||||
}
|
||||
|
|
@ -71,7 +75,11 @@ String EditorPaths::get_export_templates_dir() const {
|
|||
}
|
||||
|
||||
String EditorPaths::get_debug_keystore_path() const {
|
||||
#ifdef ANDROID_ENABLED
|
||||
return "assets://keystores/debug.keystore";
|
||||
#else
|
||||
return get_data_dir().path_join("keystores/debug.keystore");
|
||||
#endif
|
||||
}
|
||||
|
||||
String EditorPaths::get_project_settings_dir() const {
|
||||
|
|
@ -101,6 +109,7 @@ void EditorPaths::create() {
|
|||
void EditorPaths::free() {
|
||||
ERR_FAIL_NULL(singleton);
|
||||
memdelete(singleton);
|
||||
singleton = nullptr;
|
||||
}
|
||||
|
||||
void EditorPaths::_bind_methods() {
|
||||
|
|
@ -156,6 +165,7 @@ EditorPaths::EditorPaths() {
|
|||
config_dir = data_dir;
|
||||
cache_path = exe_path;
|
||||
cache_dir = data_dir.path_join("cache");
|
||||
temp_dir = data_dir.path_join("temp");
|
||||
} else {
|
||||
// Typically XDG_DATA_HOME or %APPDATA%.
|
||||
data_path = OS::get_singleton()->get_data_path();
|
||||
|
|
@ -170,6 +180,7 @@ EditorPaths::EditorPaths() {
|
|||
} else {
|
||||
cache_dir = cache_path.path_join(OS::get_singleton()->get_godot_dir_name());
|
||||
}
|
||||
temp_dir = OS::get_singleton()->get_temp_path();
|
||||
}
|
||||
|
||||
paths_valid = (!data_path.is_empty() && !config_path.is_empty() && !cache_path.is_empty());
|
||||
|
|
@ -226,6 +237,17 @@ EditorPaths::EditorPaths() {
|
|||
}
|
||||
}
|
||||
|
||||
// Temporary dir.
|
||||
{
|
||||
if (dir->change_dir(temp_dir) != OK) {
|
||||
dir->make_dir_recursive(temp_dir);
|
||||
if (dir->change_dir(temp_dir) != OK) {
|
||||
ERR_PRINT("Could not create editor temporary directory: " + temp_dir);
|
||||
paths_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate or create project-specific editor data dir,
|
||||
// including shader cache subdir.
|
||||
if (Engine::get_singleton()->is_project_manager_hint() || (Main::is_cmdline_tool() && !ProjectSettings::get_singleton()->is_project_loaded())) {
|
||||
|
|
@ -241,7 +263,7 @@ EditorPaths::EditorPaths() {
|
|||
}
|
||||
}
|
||||
|
||||
// Check that the project data directory '.gdignore' file exists
|
||||
// Check that the project data directory `.gdignore` file exists.
|
||||
String project_data_gdignore_file_path = project_data_dir.path_join(".gdignore");
|
||||
if (!FileAccess::exists(project_data_gdignore_file_path)) {
|
||||
// Add an empty .gdignore file to avoid scan.
|
||||
|
|
@ -249,7 +271,7 @@ EditorPaths::EditorPaths() {
|
|||
if (f.is_valid()) {
|
||||
f->store_line("");
|
||||
} else {
|
||||
ERR_PRINT("Failed to create file " + project_data_gdignore_file_path);
|
||||
ERR_PRINT("Failed to create file " + project_data_gdignore_file_path.quote() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class EditorPaths : public Object {
|
|||
String data_dir; // Editor data (templates, shader cache, etc.).
|
||||
String config_dir; // Editor config (settings, profiles, themes, etc.).
|
||||
String cache_dir; // Editor cache (thumbnails, tmp generated files).
|
||||
String temp_dir; // Editor temporary directory.
|
||||
String project_data_dir; // Project-specific data (metadata, shader cache, etc.).
|
||||
bool self_contained = false; // Self-contained means everything goes to `editor_data` dir.
|
||||
String self_contained_file; // Self-contained file with configuration.
|
||||
|
|
@ -61,6 +62,7 @@ public:
|
|||
String get_data_dir() const;
|
||||
String get_config_dir() const;
|
||||
String get_cache_dir() const;
|
||||
String get_temp_dir() const;
|
||||
String get_project_data_dir() const;
|
||||
String get_export_templates_dir() const;
|
||||
String get_debug_keystore_path() const;
|
||||
|
|
|
|||
|
|
@ -47,15 +47,14 @@
|
|||
#include "editor/property_selector.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
#include "scene/2d/gpu_particles_2d.h"
|
||||
#include "scene/3d/fog_volume.h"
|
||||
#include "scene/3d/gpu_particles_3d.h"
|
||||
#include "scene/gui/color_picker.h"
|
||||
#include "scene/gui/grid_container.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/resources/visual_shader_nodes.h"
|
||||
|
||||
///////////////////// Nil /////////////////////////
|
||||
|
|
@ -81,7 +80,6 @@ void EditorPropertyText::_text_submitted(const String &p_string) {
|
|||
}
|
||||
|
||||
if (text->has_focus()) {
|
||||
text->release_focus();
|
||||
_text_changed(p_string);
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +89,10 @@ void EditorPropertyText::_text_changed(const String &p_string) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Set tooltip so that the full text is displayed in a tooltip if hovered.
|
||||
// This is useful when using a narrow inspector, as the text can be trimmed otherwise.
|
||||
text->set_tooltip_text(get_tooltip_string(text->get_text()));
|
||||
|
||||
if (string_name) {
|
||||
emit_changed(get_edited_property(), StringName(p_string));
|
||||
} else {
|
||||
|
|
@ -104,6 +106,7 @@ void EditorPropertyText::update_property() {
|
|||
if (text->get_text() != s) {
|
||||
int caret = text->get_caret_column();
|
||||
text->set_text(s);
|
||||
text->set_tooltip_text(get_tooltip_string(s));
|
||||
text->set_caret_column(caret);
|
||||
}
|
||||
text->set_editable(!is_read_only());
|
||||
|
|
@ -129,9 +132,6 @@ void EditorPropertyText::set_placeholder(const String &p_string) {
|
|||
text->set_placeholder(p_string);
|
||||
}
|
||||
|
||||
void EditorPropertyText::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyText::EditorPropertyText() {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
add_child(hb);
|
||||
|
|
@ -141,7 +141,7 @@ EditorPropertyText::EditorPropertyText() {
|
|||
add_focusable(text);
|
||||
text->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyText::_text_changed));
|
||||
text->connect("text_submitted", callable_mp(this, &EditorPropertyText::_text_submitted));
|
||||
text->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyText::_text_submitted));
|
||||
}
|
||||
|
||||
///////////////////// MULTILINE TEXT /////////////////////////
|
||||
|
|
@ -153,10 +153,14 @@ void EditorPropertyMultilineText::_set_read_only(bool p_read_only) {
|
|||
|
||||
void EditorPropertyMultilineText::_big_text_changed() {
|
||||
text->set_text(big_text->get_text());
|
||||
// Set tooltip so that the full text is displayed in a tooltip if hovered.
|
||||
// This is useful when using a narrow inspector, as the text can be trimmed otherwise.
|
||||
text->set_tooltip_text(get_tooltip_string(big_text->get_text()));
|
||||
emit_changed(get_edited_property(), big_text->get_text(), "", true);
|
||||
}
|
||||
|
||||
void EditorPropertyMultilineText::_text_changed() {
|
||||
text->set_tooltip_text(get_tooltip_string(text->get_text()));
|
||||
emit_changed(get_edited_property(), text->get_text(), "", true);
|
||||
}
|
||||
|
||||
|
|
@ -185,6 +189,7 @@ void EditorPropertyMultilineText::update_property() {
|
|||
String t = get_edited_property_value();
|
||||
if (text->get_text() != t) {
|
||||
text->set_text(t);
|
||||
text->set_tooltip_text(get_tooltip_string(t));
|
||||
if (big_text && big_text->is_visible_in_tree()) {
|
||||
big_text->set_text(t);
|
||||
}
|
||||
|
|
@ -196,7 +201,7 @@ void EditorPropertyMultilineText::_notification(int p_what) {
|
|||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
Ref<Texture2D> df = get_editor_theme_icon(SNAME("DistractionFree"));
|
||||
open_big_text->set_icon(df);
|
||||
open_big_text->set_button_icon(df);
|
||||
|
||||
Ref<Font> font;
|
||||
int font_size;
|
||||
|
|
@ -219,9 +224,6 @@ void EditorPropertyMultilineText::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyMultilineText::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->add_theme_constant_override("separation", 0);
|
||||
|
|
@ -319,6 +321,9 @@ void EditorPropertyTextEnum::update_property() {
|
|||
}
|
||||
} else {
|
||||
option_button->select(default_option);
|
||||
if (default_option < 0) {
|
||||
option_button->set_text(current_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,16 +348,13 @@ void EditorPropertyTextEnum::setup(const Vector<String> &p_options, bool p_strin
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyTextEnum::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyTextEnum::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
edit_button->set_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
accept_button->set_icon(get_editor_theme_icon(SNAME("ImportCheck")));
|
||||
cancel_button->set_icon(get_editor_theme_icon(SNAME("ImportFail")));
|
||||
edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
accept_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck")));
|
||||
cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -387,7 +389,7 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() {
|
|||
custom_value_edit = memnew(LineEdit);
|
||||
custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
edit_custom_layout->add_child(custom_value_edit);
|
||||
custom_value_edit->connect("text_submitted", callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted));
|
||||
custom_value_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted));
|
||||
|
||||
accept_button = memnew(Button);
|
||||
accept_button->set_flat(true);
|
||||
|
|
@ -438,7 +440,7 @@ void EditorPropertyLocale::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
locale_edit->set_icon(get_editor_theme_icon(SNAME("Translation")));
|
||||
locale_edit->set_button_icon(get_editor_theme_icon(SNAME("Translation")));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -447,15 +449,12 @@ void EditorPropertyLocale::_locale_focus_exited() {
|
|||
_locale_selected(locale->get_text());
|
||||
}
|
||||
|
||||
void EditorPropertyLocale::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyLocale::EditorPropertyLocale() {
|
||||
HBoxContainer *locale_hb = memnew(HBoxContainer);
|
||||
add_child(locale_hb);
|
||||
locale = memnew(LineEdit);
|
||||
locale_hb->add_child(locale);
|
||||
locale->connect("text_submitted", callable_mp(this, &EditorPropertyLocale::_locale_selected));
|
||||
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);
|
||||
|
||||
|
|
@ -475,10 +474,28 @@ void EditorPropertyPath::_set_read_only(bool p_read_only) {
|
|||
}
|
||||
|
||||
void EditorPropertyPath::_path_selected(const String &p_path) {
|
||||
emit_changed(get_edited_property(), p_path);
|
||||
String full_path = p_path;
|
||||
|
||||
if (!global) {
|
||||
const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);
|
||||
if (id != ResourceUID::INVALID_ID) {
|
||||
full_path = ResourceUID::get_singleton()->id_to_text(id);
|
||||
}
|
||||
}
|
||||
|
||||
emit_changed(get_edited_property(), full_path);
|
||||
update_property();
|
||||
}
|
||||
|
||||
String EditorPropertyPath::_get_path_text() {
|
||||
String full_path = get_edited_property_value();
|
||||
if (full_path.begins_with("uid://")) {
|
||||
full_path = ResourceUID::uid_to_path(full_path);
|
||||
}
|
||||
|
||||
return full_path;
|
||||
}
|
||||
|
||||
void EditorPropertyPath::_path_pressed() {
|
||||
if (!dialog) {
|
||||
dialog = memnew(EditorFileDialog);
|
||||
|
|
@ -487,7 +504,7 @@ void EditorPropertyPath::_path_pressed() {
|
|||
add_child(dialog);
|
||||
}
|
||||
|
||||
String full_path = get_edited_property_value();
|
||||
String full_path = _get_path_text();
|
||||
|
||||
dialog->clear_filters();
|
||||
|
||||
|
|
@ -515,7 +532,7 @@ void EditorPropertyPath::_path_pressed() {
|
|||
}
|
||||
|
||||
void EditorPropertyPath::update_property() {
|
||||
String full_path = get_edited_property_value();
|
||||
String full_path = _get_path_text();
|
||||
path->set_text(full_path);
|
||||
path->set_tooltip_text(full_path);
|
||||
}
|
||||
|
|
@ -535,9 +552,9 @@ void EditorPropertyPath::_notification(int p_what) {
|
|||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
if (folder) {
|
||||
path_edit->set_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
|
||||
path_edit->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse")));
|
||||
} else {
|
||||
path_edit->set_icon(get_editor_theme_icon(SNAME("FileBrowse")));
|
||||
path_edit->set_button_icon(get_editor_theme_icon(SNAME("FileBrowse")));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
@ -560,8 +577,7 @@ void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_d
|
|||
return;
|
||||
}
|
||||
|
||||
emit_changed(get_edited_property(), filesPaths[0]);
|
||||
update_property();
|
||||
_path_selected(filesPaths[0]);
|
||||
}
|
||||
|
||||
bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
|
||||
|
|
@ -586,9 +602,6 @@ bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant
|
|||
return false;
|
||||
}
|
||||
|
||||
void EditorPropertyPath::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyPath::EditorPropertyPath() {
|
||||
HBoxContainer *path_hb = memnew(HBoxContainer);
|
||||
add_child(path_hb);
|
||||
|
|
@ -596,7 +609,7 @@ EditorPropertyPath::EditorPropertyPath() {
|
|||
SET_DRAG_FORWARDING_CDU(path, EditorPropertyPath);
|
||||
path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
|
||||
path_hb->add_child(path);
|
||||
path->connect("text_submitted", callable_mp(this, &EditorPropertyPath::_path_selected));
|
||||
path->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyPath::_path_selected));
|
||||
path->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyPath::_path_focus_exited));
|
||||
path->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
|
||||
|
|
@ -637,9 +650,6 @@ void EditorPropertyClassName::_dialog_created() {
|
|||
update_property();
|
||||
}
|
||||
|
||||
void EditorPropertyClassName::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyClassName::EditorPropertyClassName() {
|
||||
property = memnew(Button);
|
||||
property->set_clip_text(true);
|
||||
|
|
@ -669,9 +679,6 @@ void EditorPropertyCheck::update_property() {
|
|||
checkbox->set_disabled(is_read_only());
|
||||
}
|
||||
|
||||
void EditorPropertyCheck::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyCheck::EditorPropertyCheck() {
|
||||
checkbox = memnew(CheckBox);
|
||||
checkbox->set_text(TTR("On"));
|
||||
|
|
@ -695,6 +702,7 @@ void EditorPropertyEnum::update_property() {
|
|||
Variant current = get_edited_property_value();
|
||||
if (current.get_type() == Variant::NIL) {
|
||||
options->select(-1);
|
||||
options->set_text("<null>");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -705,29 +713,33 @@ void EditorPropertyEnum::update_property() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
options->select(-1);
|
||||
options->set_text(itos(which));
|
||||
}
|
||||
|
||||
void EditorPropertyEnum::setup(const Vector<String> &p_options) {
|
||||
options->clear();
|
||||
HashMap<int64_t, Vector<String>> items;
|
||||
int64_t current_val = 0;
|
||||
for (int i = 0; i < p_options.size(); i++) {
|
||||
Vector<String> text_split = p_options[i].split(":");
|
||||
for (const String &option : p_options) {
|
||||
Vector<String> text_split = option.split(":");
|
||||
if (text_split.size() != 1) {
|
||||
current_val = text_split[1].to_int();
|
||||
}
|
||||
options->add_item(text_split[0]);
|
||||
options->set_item_metadata(i, current_val);
|
||||
items[current_val].push_back(text_split[0]);
|
||||
current_val += 1;
|
||||
}
|
||||
|
||||
for (const KeyValue<int64_t, Vector<String>> &K : items) {
|
||||
options->add_item(String(", ").join(K.value));
|
||||
options->set_item_metadata(-1, K.key);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyEnum::set_option_button_clip(bool p_enable) {
|
||||
options->set_clip_text(p_enable);
|
||||
}
|
||||
|
||||
void EditorPropertyEnum::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyEnum::EditorPropertyEnum() {
|
||||
options = memnew(OptionButton);
|
||||
options->set_clip_text(true);
|
||||
|
|
@ -804,9 +816,6 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyFlags::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyFlags::EditorPropertyFlags() {
|
||||
vbox = memnew(VBoxContainer);
|
||||
add_child(vbox);
|
||||
|
|
@ -832,7 +841,7 @@ void EditorPropertyLayersGrid::_rename_operation_confirm() {
|
|||
if (new_name.length() == 0) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
|
||||
return;
|
||||
} else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
|
||||
} else if (new_name.contains_char('/') || new_name.contains_char('\\') || new_name.contains_char(':')) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
|
||||
return;
|
||||
}
|
||||
|
|
@ -1280,9 +1289,6 @@ void EditorPropertyLayers::_refresh_names() {
|
|||
setup(layer_type);
|
||||
}
|
||||
|
||||
void EditorPropertyLayers::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyLayers::EditorPropertyLayers() {
|
||||
HBoxContainer *hb = memnew(HBoxContainer);
|
||||
hb->set_clip_contents(true);
|
||||
|
|
@ -1330,9 +1336,6 @@ void EditorPropertyInteger::update_property() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void EditorPropertyInteger::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
|
||||
spin->set_min(p_min);
|
||||
spin->set_max(p_max);
|
||||
|
|
@ -1346,6 +1349,7 @@ void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step,
|
|||
EditorPropertyInteger::EditorPropertyInteger() {
|
||||
spin = memnew(EditorSpinSlider);
|
||||
spin->set_flat(true);
|
||||
spin->set_editing_integer(true);
|
||||
add_child(spin);
|
||||
add_focusable(spin);
|
||||
spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyInteger::_value_changed));
|
||||
|
|
@ -1372,12 +1376,12 @@ void EditorPropertyObjectID::update_property() {
|
|||
edit->set_text(type + " ID: " + uitos(id));
|
||||
edit->set_tooltip_text(type + " ID: " + uitos(id));
|
||||
edit->set_disabled(false);
|
||||
edit->set_icon(EditorNode::get_singleton()->get_class_icon(type));
|
||||
edit->set_button_icon(EditorNode::get_singleton()->get_class_icon(type));
|
||||
} else {
|
||||
edit->set_text(TTR("<empty>"));
|
||||
edit->set_tooltip_text("");
|
||||
edit->set_disabled(true);
|
||||
edit->set_icon(Ref<Texture2D>());
|
||||
edit->set_button_icon(Ref<Texture2D>());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1385,9 +1389,6 @@ void EditorPropertyObjectID::setup(const String &p_base_type) {
|
|||
base_type = p_base_type;
|
||||
}
|
||||
|
||||
void EditorPropertyObjectID::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyObjectID::EditorPropertyObjectID() {
|
||||
edit = memnew(Button);
|
||||
add_child(edit);
|
||||
|
|
@ -1410,10 +1411,7 @@ void EditorPropertySignal::update_property() {
|
|||
|
||||
edit->set_text("Signal: " + signal.get_name());
|
||||
edit->set_disabled(false);
|
||||
edit->set_icon(get_editor_theme_icon(SNAME("Signals")));
|
||||
}
|
||||
|
||||
void EditorPropertySignal::_bind_methods() {
|
||||
edit->set_button_icon(get_editor_theme_icon(SNAME("Signals")));
|
||||
}
|
||||
|
||||
EditorPropertySignal::EditorPropertySignal() {
|
||||
|
|
@ -1432,10 +1430,7 @@ void EditorPropertyCallable::update_property() {
|
|||
|
||||
edit->set_text("Callable");
|
||||
edit->set_disabled(true);
|
||||
edit->set_icon(get_editor_theme_icon(SNAME("Callable")));
|
||||
}
|
||||
|
||||
void EditorPropertyCallable::_bind_methods() {
|
||||
edit->set_button_icon(get_editor_theme_icon(SNAME("Callable")));
|
||||
}
|
||||
|
||||
EditorPropertyCallable::EditorPropertyCallable() {
|
||||
|
|
@ -1465,9 +1460,6 @@ void EditorPropertyFloat::update_property() {
|
|||
spin->set_value_no_signal(val);
|
||||
}
|
||||
|
||||
void EditorPropertyFloat::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_exp_range, bool p_greater, bool p_lesser, const String &p_suffix, bool p_radians_as_degrees) {
|
||||
radians_as_degrees = p_radians_as_degrees;
|
||||
spin->set_min(p_min);
|
||||
|
|
@ -1675,9 +1667,6 @@ void EditorPropertyEasing::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyEasing::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyEasing::EditorPropertyEasing() {
|
||||
easing_draw = memnew(Control);
|
||||
easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing));
|
||||
|
|
@ -1740,9 +1729,6 @@ void EditorPropertyRect2::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyRect2::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -1837,9 +1823,6 @@ void EditorPropertyRect2i::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyRect2i::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -1848,6 +1831,7 @@ void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {
|
|||
spin[i]->set_allow_greater(true);
|
||||
spin[i]->set_allow_lesser(true);
|
||||
spin[i]->set_suffix(p_suffix);
|
||||
spin[i]->set_editing_integer(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1933,9 +1917,6 @@ void EditorPropertyPlane::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyPlane::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2077,17 +2058,14 @@ void EditorPropertyQuaternion::_notification(int p_what) {
|
|||
for (int i = 0; i < 3; i++) {
|
||||
euler[i]->add_theme_color_override("label_color", colors[i]);
|
||||
}
|
||||
edit_button->set_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
|
||||
euler_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("property_color"), SNAME("EditorProperty")));
|
||||
warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
|
||||
warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyQuaternion::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix, bool p_hide_editor) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2232,9 +2210,6 @@ void EditorPropertyAABB::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyAABB::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2313,9 +2288,6 @@ void EditorPropertyTransform2D::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyTransform2D::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2398,9 +2370,6 @@ void EditorPropertyBasis::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyBasis::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2490,9 +2459,6 @@ void EditorPropertyTransform3D::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyTransform3D::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyTransform3D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2590,9 +2556,6 @@ void EditorPropertyProjection::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyProjection::_bind_methods() {
|
||||
}
|
||||
|
||||
void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
spin[i]->set_min(p_min);
|
||||
|
|
@ -2644,6 +2607,17 @@ void EditorPropertyColor::_color_changed(const Color &p_color) {
|
|||
get_edited_object()->set(get_edited_property(), p_color);
|
||||
}
|
||||
|
||||
void EditorPropertyColor::_picker_created() {
|
||||
picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_popup_opening));
|
||||
picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
void EditorPropertyColor::_popup_opening() {
|
||||
EditorNode::get_singleton()->setup_color_picker(picker->get_picker());
|
||||
last_color = picker->get_pick_color();
|
||||
was_checked = !is_checkable() || is_checked();
|
||||
}
|
||||
|
||||
void EditorPropertyColor::_popup_closed() {
|
||||
get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant());
|
||||
if (!picker->get_pick_color().is_equal_approx(last_color)) {
|
||||
|
|
@ -2651,11 +2625,6 @@ void EditorPropertyColor::_popup_closed() {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyColor::_picker_opening() {
|
||||
last_color = picker->get_pick_color();
|
||||
was_checked = !is_checkable() || is_checked();
|
||||
}
|
||||
|
||||
void EditorPropertyColor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
|
|
@ -2699,9 +2668,7 @@ EditorPropertyColor::EditorPropertyColor() {
|
|||
add_child(picker);
|
||||
picker->set_flat(true);
|
||||
picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed));
|
||||
picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed));
|
||||
picker->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(picker->get_picker()));
|
||||
picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_picker_opening));
|
||||
picker->connect("picker_created", callable_mp(this, &EditorPropertyColor::_picker_created), CONNECT_ONE_SHOT);
|
||||
}
|
||||
|
||||
////////////// NODE PATH //////////////////////
|
||||
|
|
@ -2709,7 +2676,7 @@ EditorPropertyColor::EditorPropertyColor() {
|
|||
void EditorPropertyNodePath::_set_read_only(bool p_read_only) {
|
||||
assign->set_disabled(p_read_only);
|
||||
menu->set_disabled(p_read_only);
|
||||
};
|
||||
}
|
||||
|
||||
Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool &r_valid) const {
|
||||
if (p_prop == get_edited_property()) {
|
||||
|
|
@ -2719,7 +2686,7 @@ Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool
|
|||
return Variant();
|
||||
}
|
||||
|
||||
void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
|
||||
void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) {
|
||||
NodePath path = p_path;
|
||||
Node *base_node = get_base_node();
|
||||
|
||||
|
|
@ -2729,7 +2696,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
|
|||
path = get_tree()->get_edited_scene_root()->get_path_to(to_node);
|
||||
}
|
||||
|
||||
if (base_node) { // for AnimationTrackKeyEdit
|
||||
if (p_absolute && base_node) { // for AnimationTrackKeyEdit
|
||||
path = base_node->get_path().rel_path_to(p_path);
|
||||
}
|
||||
|
||||
|
|
@ -2751,7 +2718,7 @@ void EditorPropertyNodePath::_node_assign() {
|
|||
scene_tree->get_scene_tree()->set_show_enabled_subscene(true);
|
||||
scene_tree->set_valid_types(valid_types);
|
||||
add_child(scene_tree);
|
||||
scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected));
|
||||
scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true));
|
||||
}
|
||||
|
||||
Variant val = get_edited_property_value();
|
||||
|
|
@ -2778,7 +2745,11 @@ void EditorPropertyNodePath::_update_menu() {
|
|||
void EditorPropertyNodePath::_menu_option(int p_idx) {
|
||||
switch (p_idx) {
|
||||
case ACTION_CLEAR: {
|
||||
emit_changed(get_edited_property(), NodePath());
|
||||
if (editing_node) {
|
||||
emit_changed(get_edited_property(), Variant());
|
||||
} else {
|
||||
emit_changed(get_edited_property(), NodePath());
|
||||
}
|
||||
update_property();
|
||||
} break;
|
||||
|
||||
|
|
@ -2815,7 +2786,7 @@ void EditorPropertyNodePath::_accept_text() {
|
|||
|
||||
void EditorPropertyNodePath::_text_submitted(const String &p_text) {
|
||||
NodePath np = p_text;
|
||||
emit_changed(get_edited_property(), np);
|
||||
_node_selected(np, false);
|
||||
edit->hide();
|
||||
assign->show();
|
||||
menu->show();
|
||||
|
|
@ -2896,7 +2867,7 @@ void EditorPropertyNodePath::update_property() {
|
|||
assign->set_tooltip_text(p);
|
||||
|
||||
if (p.is_empty()) {
|
||||
assign->set_icon(Ref<Texture2D>());
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(TTR("Assign..."));
|
||||
assign->set_flat(false);
|
||||
return;
|
||||
|
|
@ -2904,7 +2875,7 @@ void EditorPropertyNodePath::update_property() {
|
|||
assign->set_flat(true);
|
||||
|
||||
if (!base_node || !base_node->has_node(p)) {
|
||||
assign->set_icon(Ref<Texture2D>());
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(p);
|
||||
return;
|
||||
}
|
||||
|
|
@ -2912,14 +2883,14 @@ void EditorPropertyNodePath::update_property() {
|
|||
const Node *target_node = base_node->get_node(p);
|
||||
ERR_FAIL_NULL(target_node);
|
||||
|
||||
if (String(target_node->get_name()).contains("@")) {
|
||||
assign->set_icon(Ref<Texture2D>());
|
||||
if (String(target_node->get_name()).contains_char('@')) {
|
||||
assign->set_button_icon(Ref<Texture2D>());
|
||||
assign->set_text(p);
|
||||
return;
|
||||
}
|
||||
|
||||
assign->set_text(target_node->get_name());
|
||||
assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node"));
|
||||
assign->set_button_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node"));
|
||||
}
|
||||
|
||||
void EditorPropertyNodePath::setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) {
|
||||
|
|
@ -2932,7 +2903,7 @@ void EditorPropertyNodePath::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
|
||||
menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
|
||||
menu->get_popup()->set_item_icon(ACTION_CLEAR, get_editor_theme_icon(SNAME("Clear")));
|
||||
menu->get_popup()->set_item_icon(ACTION_COPY, get_editor_theme_icon(SNAME("ActionCopy")));
|
||||
menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit")));
|
||||
|
|
@ -2996,7 +2967,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
|
|||
edit->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
edit->hide();
|
||||
edit->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyNodePath::_accept_text));
|
||||
edit->connect(SNAME("text_submitted"), callable_mp(this, &EditorPropertyNodePath::_text_submitted));
|
||||
edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyNodePath::_text_submitted));
|
||||
hbc->add_child(edit);
|
||||
}
|
||||
|
||||
|
|
@ -3246,6 +3217,10 @@ void EditorPropertyResource::_update_preferred_shader() {
|
|||
}
|
||||
}
|
||||
|
||||
bool EditorPropertyResource::_should_stop_editing() const {
|
||||
return !resource_picker->is_toggle_pressed();
|
||||
}
|
||||
|
||||
void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
|
||||
Node *to_node = get_node(p_path);
|
||||
if (!Object::cast_to<Viewport>(to_node)) {
|
||||
|
|
@ -3284,6 +3259,7 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const
|
|||
}
|
||||
|
||||
resource_picker->set_base_type(p_base_type);
|
||||
resource_picker->set_resource_owner(p_object);
|
||||
resource_picker->set_editable(true);
|
||||
resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
add_child(resource_picker);
|
||||
|
|
@ -3313,7 +3289,10 @@ void EditorPropertyResource::update_property() {
|
|||
sub_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
|
||||
sub_inspector->set_use_doc_hints(true);
|
||||
|
||||
sub_inspector->set_sub_inspector(true);
|
||||
EditorInspector *parent_inspector = get_parent_inspector();
|
||||
ERR_FAIL_NULL(parent_inspector);
|
||||
sub_inspector->set_root_inspector(parent_inspector->get_root_inspector());
|
||||
|
||||
sub_inspector->set_property_name_style(InspectorDock::get_singleton()->get_property_name_style());
|
||||
|
||||
sub_inspector->connect("property_keyed", callable_mp(this, &EditorPropertyResource::_sub_inspector_property_keyed));
|
||||
|
|
@ -3323,6 +3302,11 @@ void EditorPropertyResource::update_property() {
|
|||
sub_inspector->set_read_only(is_read_only());
|
||||
sub_inspector->set_use_folding(is_using_folding());
|
||||
|
||||
sub_inspector->set_draw_focus_border(false);
|
||||
|
||||
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);
|
||||
|
|
@ -3349,16 +3333,14 @@ void EditorPropertyResource::update_property() {
|
|||
_update_property_bg();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (sub_inspector) {
|
||||
set_bottom_editor(nullptr);
|
||||
memdelete(sub_inspector);
|
||||
sub_inspector = nullptr;
|
||||
} else if (sub_inspector) {
|
||||
set_bottom_editor(nullptr);
|
||||
memdelete(sub_inspector);
|
||||
sub_inspector = nullptr;
|
||||
|
||||
if (opened_editor) {
|
||||
EditorNode::get_singleton()->hide_unused_editors();
|
||||
opened_editor = false;
|
||||
}
|
||||
if (opened_editor) {
|
||||
EditorNode::get_singleton()->hide_unused_editors();
|
||||
opened_editor = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3388,6 +3370,13 @@ void EditorPropertyResource::set_use_sub_inspector(bool p_enable) {
|
|||
use_sub_inspector = p_enable;
|
||||
}
|
||||
|
||||
void EditorPropertyResource::set_use_filter(bool p_use) {
|
||||
use_filter = p_use;
|
||||
if (sub_inspector) {
|
||||
update_property();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyResource::fold_resource() {
|
||||
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
|
||||
if (unfolded) {
|
||||
|
|
@ -3417,13 +3406,18 @@ void EditorPropertyResource::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
const EditorInspector *ei = get_parent_inspector();
|
||||
if (ei && !ei->is_main_editor_inspector()) {
|
||||
const EditorInspector *main_ei = InspectorDock::get_inspector_singleton();
|
||||
if (ei && main_ei && ei != main_ei && !main_ei->is_ancestor_of(ei)) {
|
||||
fold_resource();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyResource::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_should_stop_editing"), &EditorPropertyResource::_should_stop_editing);
|
||||
}
|
||||
|
||||
EditorPropertyResource::EditorPropertyResource() {
|
||||
use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));
|
||||
has_borders = true;
|
||||
|
|
@ -3676,7 +3670,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
|
|||
case Variant::VECTOR2I: {
|
||||
EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
|
||||
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
|
||||
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
|
||||
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);
|
||||
return editor;
|
||||
|
||||
} break;
|
||||
|
|
@ -3703,7 +3697,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
|
|||
case Variant::VECTOR3I: {
|
||||
EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
|
||||
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
|
||||
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
|
||||
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);
|
||||
return editor;
|
||||
|
||||
} break;
|
||||
|
|
@ -3717,7 +3711,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
|
|||
case Variant::VECTOR4I: {
|
||||
EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
|
||||
EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
|
||||
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix);
|
||||
editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);
|
||||
return editor;
|
||||
|
||||
} break;
|
||||
|
|
@ -3846,7 +3840,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
|
|||
return editor;
|
||||
} else {
|
||||
EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary);
|
||||
editor->setup(p_hint);
|
||||
editor->setup(p_hint, p_hint_text);
|
||||
return editor;
|
||||
}
|
||||
} break;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ class EditorPropertyText : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_string_name(bool p_enabled);
|
||||
|
|
@ -92,7 +91,6 @@ class EditorPropertyMultilineText : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -126,7 +124,6 @@ class EditorPropertyTextEnum : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
|
|
@ -145,6 +142,8 @@ class EditorPropertyPath : public EditorProperty {
|
|||
LineEdit *path = nullptr;
|
||||
Button *path_edit = nullptr;
|
||||
|
||||
String _get_path_text();
|
||||
|
||||
void _path_selected(const String &p_path);
|
||||
void _path_pressed();
|
||||
void _path_focus_exited();
|
||||
|
|
@ -153,7 +152,6 @@ class EditorPropertyPath : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
|
|
@ -174,7 +172,6 @@ class EditorPropertyLocale : public EditorProperty {
|
|||
void _locale_focus_exited();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
|
|
@ -196,7 +193,6 @@ private:
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void setup(const String &p_base_type, const String &p_selected_type);
|
||||
|
|
@ -212,7 +208,6 @@ class EditorPropertyCheck : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -227,7 +222,6 @@ class EditorPropertyEnum : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void setup(const Vector<String> &p_options);
|
||||
|
|
@ -246,7 +240,6 @@ class EditorPropertyFlags : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void setup(const Vector<String> &p_options);
|
||||
|
|
@ -327,7 +320,6 @@ private:
|
|||
protected:
|
||||
void _notification(int p_what);
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void setup(LayerType p_layer_type);
|
||||
|
|
@ -344,7 +336,6 @@ class EditorPropertyInteger : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -360,7 +351,6 @@ class EditorPropertyObjectID : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -374,9 +364,6 @@ class EditorPropertySignal : public EditorProperty {
|
|||
String base_type;
|
||||
void _edit_pressed();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
EditorPropertySignal();
|
||||
|
|
@ -387,9 +374,6 @@ class EditorPropertyCallable : public EditorProperty {
|
|||
Button *edit = nullptr;
|
||||
String base_type;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
EditorPropertyCallable();
|
||||
|
|
@ -403,7 +387,6 @@ class EditorPropertyFloat : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -445,7 +428,6 @@ class EditorPropertyEasing : public EditorProperty {
|
|||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -461,7 +443,6 @@ class EditorPropertyRect2 : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -477,7 +458,6 @@ class EditorPropertyRect2i : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -493,7 +473,6 @@ class EditorPropertyPlane : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -526,7 +505,6 @@ class EditorPropertyQuaternion : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -542,7 +520,6 @@ class EditorPropertyAABB : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -558,7 +535,6 @@ class EditorPropertyTransform2D : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -574,7 +550,6 @@ class EditorPropertyBasis : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -590,7 +565,6 @@ class EditorPropertyTransform3D : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -607,7 +581,6 @@ class EditorPropertyProjection : public EditorProperty {
|
|||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -620,8 +593,9 @@ class EditorPropertyColor : public EditorProperty {
|
|||
GDCLASS(EditorPropertyColor, EditorProperty);
|
||||
ColorPickerButton *picker = nullptr;
|
||||
void _color_changed(const Color &p_color);
|
||||
void _picker_created();
|
||||
void _popup_opening();
|
||||
void _popup_closed();
|
||||
void _picker_opening();
|
||||
|
||||
Color last_color;
|
||||
bool live_changes_enabled = true;
|
||||
|
|
@ -657,7 +631,7 @@ class EditorPropertyNodePath : public EditorProperty {
|
|||
bool editing_node = false;
|
||||
|
||||
Vector<StringName> valid_types;
|
||||
void _node_selected(const NodePath &p_path);
|
||||
void _node_selected(const NodePath &p_path, bool p_absolute = true);
|
||||
void _node_assign();
|
||||
Node *get_base_node();
|
||||
void _update_menu();
|
||||
|
|
@ -700,6 +674,7 @@ class EditorPropertyResource : public EditorProperty {
|
|||
bool use_sub_inspector = false;
|
||||
EditorInspector *sub_inspector = nullptr;
|
||||
bool opened_editor = false;
|
||||
bool use_filter = false;
|
||||
|
||||
void _resource_selected(const Ref<Resource> &p_resource, bool p_inspect);
|
||||
void _resource_changed(const Ref<Resource> &p_resource);
|
||||
|
|
@ -712,10 +687,12 @@ class EditorPropertyResource : public EditorProperty {
|
|||
|
||||
void _open_editor_pressed();
|
||||
void _update_preferred_shader();
|
||||
bool _should_stop_editing() const;
|
||||
|
||||
protected:
|
||||
virtual void _set_read_only(bool p_read_only) override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
|
|
@ -726,6 +703,7 @@ public:
|
|||
void expand_revertable() override;
|
||||
|
||||
void set_use_sub_inspector(bool p_enable);
|
||||
void set_use_filter(bool p_use);
|
||||
void fold_resource();
|
||||
|
||||
virtual bool is_colored(ColorationMode p_mode) override;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#include "core/input/input.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_properties.h"
|
||||
#include "editor/editor_properties_vector.h"
|
||||
#include "editor/editor_settings.h"
|
||||
|
|
@ -39,10 +40,8 @@
|
|||
#include "editor/gui/editor_spin_slider.h"
|
||||
#include "editor/inspector_dock.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "editor/themes/editor_theme_manager.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) {
|
||||
String name = p_name;
|
||||
|
|
@ -146,6 +145,16 @@ bool EditorPropertyDictionaryObject::get_by_property_name(const String &p_name,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (name == "new_item_key_name") {
|
||||
r_ret = TTR("New Key:");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "new_item_value_name") {
|
||||
r_ret = TTR("New Value:");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name.begins_with("indices")) {
|
||||
int index = name.get_slicec('/', 1).to_int();
|
||||
Variant key = dict.get_key_at_index(index);
|
||||
|
|
@ -153,6 +162,13 @@ bool EditorPropertyDictionaryObject::get_by_property_name(const String &p_name,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (name.begins_with("keys")) {
|
||||
int index = name.get_slicec('/', 1).to_int();
|
||||
Variant key = dict.get_key_at_index(index);
|
||||
r_ret = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -191,6 +207,17 @@ String EditorPropertyDictionaryObject::get_property_name_for_index(int p_index)
|
|||
}
|
||||
}
|
||||
|
||||
String EditorPropertyDictionaryObject::get_key_name_for_index(int p_index) {
|
||||
switch (p_index) {
|
||||
case NEW_KEY_INDEX:
|
||||
return "new_item_key_name";
|
||||
case NEW_VALUE_INDEX:
|
||||
return "new_item_value_name";
|
||||
default:
|
||||
return "keys/" + itos(p_index);
|
||||
}
|
||||
}
|
||||
|
||||
String EditorPropertyDictionaryObject::get_label_for_index(int p_index) {
|
||||
switch (p_index) {
|
||||
case NEW_KEY_INDEX:
|
||||
|
|
@ -283,7 +310,7 @@ void EditorPropertyArray::_create_new_property_slot() {
|
|||
HBoxContainer *hbox = memnew(HBoxContainer);
|
||||
|
||||
Button *reorder_button = memnew(Button);
|
||||
reorder_button->set_icon(get_editor_theme_icon(SNAME("TripleBar")));
|
||||
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());
|
||||
reorder_button->connect(SceneStringName(gui_input), callable_mp(this, &EditorPropertyArray::_reorder_button_gui_input));
|
||||
|
|
@ -298,13 +325,13 @@ void EditorPropertyArray::_create_new_property_slot() {
|
|||
|
||||
if (is_untyped_array) {
|
||||
Button *edit_btn = memnew(Button);
|
||||
edit_btn->set_icon(get_editor_theme_icon(SNAME("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_icon(get_editor_theme_icon(SNAME("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));
|
||||
hbox->add_child(remove_btn);
|
||||
|
|
@ -320,10 +347,15 @@ void EditorPropertyArray::_create_new_property_slot() {
|
|||
slots.push_back(slot);
|
||||
}
|
||||
|
||||
void EditorPropertyArray::set_preview_value(bool p_preview_value) {
|
||||
preview_value = p_preview_value;
|
||||
}
|
||||
|
||||
void EditorPropertyArray::update_property() {
|
||||
Variant array = get_edited_property_value();
|
||||
|
||||
String array_type_name = Variant::get_type_name(array_type);
|
||||
String array_sub_type_name;
|
||||
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
|
||||
String type_name;
|
||||
if (subtype == Variant::OBJECT && (subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || subtype_hint == PROPERTY_HINT_NODE_TYPE)) {
|
||||
|
|
@ -332,11 +364,23 @@ void EditorPropertyArray::update_property() {
|
|||
type_name = Variant::get_type_name(subtype);
|
||||
}
|
||||
|
||||
array_type_name = vformat("%s[%s]", array_type_name, type_name);
|
||||
if (preview_value) {
|
||||
array_sub_type_name = vformat("[%s] ", type_name);
|
||||
} else {
|
||||
array_type_name = vformat("%s[%s]", array_type_name, type_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!array.is_array()) {
|
||||
edit->set_text(vformat(TTR("(Nil) %s"), array_type_name));
|
||||
if (preview_value) {
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
edit->set_button_icon(get_editor_theme_icon(SNAME("Nil")));
|
||||
edit->set_text(array_type_name);
|
||||
} else {
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
edit->set_button_icon(Ref<Texture2D>());
|
||||
edit->set_text(vformat(TTR("(Nil) %s"), array_type_name));
|
||||
}
|
||||
edit->set_pressed(false);
|
||||
if (container) {
|
||||
set_bottom_editor(nullptr);
|
||||
|
|
@ -356,7 +400,25 @@ void EditorPropertyArray::update_property() {
|
|||
_page_changed(max_page);
|
||||
}
|
||||
|
||||
edit->set_text(vformat(TTR("%s (size %s)"), array_type_name, itos(size)));
|
||||
if (preview_value) {
|
||||
String ctr_str = array.get_construct_string().trim_prefix(array_type_name + "(").trim_suffix(")").replace("\n", "");
|
||||
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
|
||||
int type_end = ctr_str.find("](");
|
||||
if (type_end > 0) {
|
||||
ctr_str = ctr_str.substr(type_end + 2);
|
||||
}
|
||||
}
|
||||
|
||||
edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
edit->set_button_icon(get_editor_theme_icon(array_type_name));
|
||||
edit->set_text(vformat("%s%s", array_sub_type_name, ctr_str));
|
||||
edit->set_tooltip_text(vformat(TTR("%s%s (size %d)"), array_type_name, array_sub_type_name, size));
|
||||
} else {
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
edit->set_button_icon(Ref<Texture2D>());
|
||||
edit->set_text(vformat(TTR("%s (size %d)"), array_type_name, size));
|
||||
}
|
||||
|
||||
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
|
||||
if (edit->is_pressed() != unfolded) {
|
||||
|
|
@ -385,6 +447,7 @@ void EditorPropertyArray::update_property() {
|
|||
size_slider = memnew(EditorSpinSlider);
|
||||
size_slider->set_step(1);
|
||||
size_slider->set_max(INT32_MAX);
|
||||
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->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyArray::_length_changed));
|
||||
|
|
@ -395,7 +458,7 @@ void EditorPropertyArray::update_property() {
|
|||
vbox->add_child(property_vbox);
|
||||
|
||||
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element"));
|
||||
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
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->set_disabled(is_read_only());
|
||||
vbox->add_child(button_add_item);
|
||||
|
|
@ -644,6 +707,8 @@ void EditorPropertyArray::_notification(int p_what) {
|
|||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
change_type->clear();
|
||||
change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
|
||||
change_type->add_separator();
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
|
||||
// These types can't be constructed or serialized properly, so skip them.
|
||||
|
|
@ -653,11 +718,9 @@ void EditorPropertyArray::_notification(int p_what) {
|
|||
String type = Variant::get_type_name(Variant::Type(i));
|
||||
change_type->add_icon_item(get_editor_theme_icon(type), type, i);
|
||||
}
|
||||
change_type->add_separator();
|
||||
change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
|
||||
|
||||
if (button_add_item) {
|
||||
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
}
|
||||
} break;
|
||||
|
||||
|
|
@ -740,10 +803,10 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint
|
|||
// The format of p_hint_string is:
|
||||
// subType/subTypeHint:nextSubtype ... etc.
|
||||
if (!p_hint_string.is_empty()) {
|
||||
int hint_subtype_separator = p_hint_string.find(":");
|
||||
int hint_subtype_separator = p_hint_string.find_char(':');
|
||||
if (hint_subtype_separator >= 0) {
|
||||
String subtype_string = p_hint_string.substr(0, hint_subtype_separator);
|
||||
int slash_pos = subtype_string.find("/");
|
||||
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_string = subtype_string.substr(0, slash_pos);
|
||||
|
|
@ -819,6 +882,7 @@ void EditorPropertyArray::_reorder_button_up() {
|
|||
array.call("remove_at", reorder_slot.index);
|
||||
array.call("insert", reorder_to_index, value_to_move);
|
||||
|
||||
slots[reorder_to_index % page_length].reorder_button->grab_focus();
|
||||
emit_changed(get_edited_property(), array);
|
||||
}
|
||||
|
||||
|
|
@ -836,9 +900,6 @@ bool EditorPropertyArray::is_colored(ColorationMode p_mode) {
|
|||
return p_mode == COLORATION_CONTAINER_RESOURCE;
|
||||
}
|
||||
|
||||
void EditorPropertyArray::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyArray::EditorPropertyArray() {
|
||||
object.instantiate();
|
||||
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
|
||||
|
|
@ -866,13 +927,37 @@ EditorPropertyArray::EditorPropertyArray() {
|
|||
|
||||
///////////////////// DICTIONARY ///////////////////////////
|
||||
|
||||
void EditorPropertyDictionary::initialize_dictionary(Variant &p_dictionary) {
|
||||
if (key_subtype != Variant::NIL || value_subtype != Variant::NIL) {
|
||||
Dictionary dict;
|
||||
StringName key_subtype_class;
|
||||
Ref<Script> key_subtype_script;
|
||||
if (key_subtype == Variant::OBJECT && !key_subtype_hint_string.is_empty() && ClassDB::class_exists(key_subtype_hint_string)) {
|
||||
key_subtype_class = key_subtype_hint_string;
|
||||
}
|
||||
StringName value_subtype_class;
|
||||
Ref<Script> value_subtype_script;
|
||||
if (value_subtype == Variant::OBJECT && !value_subtype_hint_string.is_empty() && ClassDB::class_exists(value_subtype_hint_string)) {
|
||||
value_subtype_class = value_subtype_hint_string;
|
||||
}
|
||||
dict.set_typed(key_subtype, key_subtype_class, key_subtype_script, value_subtype, value_subtype_class, value_subtype_script);
|
||||
p_dictionary = dict;
|
||||
} else {
|
||||
VariantInternal::initialize(&p_dictionary, Variant::DICTIONARY);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
|
||||
if (p_value.get_type() == Variant::OBJECT && p_value.is_null()) {
|
||||
p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716.
|
||||
}
|
||||
|
||||
object->set(p_property, p_value);
|
||||
emit_changed(get_edited_property(), object->get_dict(), p_name, p_changing);
|
||||
bool new_item_or_key = !p_property.begins_with("indices");
|
||||
emit_changed(get_edited_property(), object->get_dict(), p_name, p_changing || new_item_or_key);
|
||||
if (new_item_or_key) {
|
||||
update_property();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::_change_type(Object *p_button, int p_slot_index) {
|
||||
|
|
@ -914,21 +999,46 @@ void EditorPropertyDictionary::_add_key_value() {
|
|||
|
||||
void EditorPropertyDictionary::_create_new_property_slot(int p_idx) {
|
||||
HBoxContainer *hbox = memnew(HBoxContainer);
|
||||
|
||||
EditorProperty *prop_key = nullptr;
|
||||
if (p_idx != EditorPropertyDictionaryObject::NEW_KEY_INDEX && p_idx != EditorPropertyDictionaryObject::NEW_VALUE_INDEX) {
|
||||
prop_key = memnew(EditorPropertyNil);
|
||||
hbox->add_child(prop_key);
|
||||
}
|
||||
|
||||
EditorProperty *prop = memnew(EditorPropertyNil);
|
||||
prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
if (p_idx != EditorPropertyDictionaryObject::NEW_KEY_INDEX && p_idx != EditorPropertyDictionaryObject::NEW_VALUE_INDEX) {
|
||||
prop->set_draw_label(false);
|
||||
}
|
||||
hbox->add_child(prop);
|
||||
|
||||
Button *edit_btn = memnew(Button);
|
||||
edit_btn->set_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);
|
||||
bool use_key = p_idx == EditorPropertyDictionaryObject::NEW_KEY_INDEX;
|
||||
bool is_untyped_dict = (use_key ? key_subtype : value_subtype) == Variant::NIL;
|
||||
|
||||
if (is_untyped_dict) {
|
||||
Button *edit_btn = memnew(Button);
|
||||
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_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()));
|
||||
hbox->add_child(remove_btn);
|
||||
}
|
||||
|
||||
if (add_panel) {
|
||||
add_panel->get_child(0)->add_child(hbox);
|
||||
} else {
|
||||
property_vbox->add_child(hbox);
|
||||
}
|
||||
|
||||
Slot slot;
|
||||
slot.prop = prop;
|
||||
slot.prop_key = prop_key;
|
||||
slot.object = object;
|
||||
slot.container = hbox;
|
||||
int index = p_idx + (p_idx >= 0 ? page_index * page_length : 0);
|
||||
|
|
@ -972,15 +1082,87 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::setup(PropertyHint p_hint) {
|
||||
property_hint = p_hint;
|
||||
void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_string) {
|
||||
PackedStringArray types = p_hint_string.split(";");
|
||||
if (types.size() > 0 && !types[0].is_empty()) {
|
||||
String key = types[0];
|
||||
int hint_key_subtype_separator = key.find_char(':');
|
||||
if (hint_key_subtype_separator >= 0) {
|
||||
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_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 = Variant::Type(key_subtype_string.to_int());
|
||||
|
||||
Variant new_key = object->get_new_item_key();
|
||||
VariantInternal::initialize(&new_key, key_subtype);
|
||||
object->set_new_item_key(new_key);
|
||||
}
|
||||
}
|
||||
if (types.size() > 1 && !types[1].is_empty()) {
|
||||
String value = types[1];
|
||||
int hint_value_subtype_separator = value.find_char(':');
|
||||
if (hint_value_subtype_separator >= 0) {
|
||||
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_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 = Variant::Type(value_subtype_string.to_int());
|
||||
|
||||
Variant new_value = object->get_new_item_value();
|
||||
VariantInternal::initialize(&new_value, value_subtype);
|
||||
object->set_new_item_value(new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::set_preview_value(bool p_preview_value) {
|
||||
preview_value = p_preview_value;
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::update_property() {
|
||||
Variant updated_val = get_edited_property_value();
|
||||
|
||||
String dict_type_name = "Dictionary";
|
||||
String dict_sub_type_name;
|
||||
if (key_subtype != Variant::NIL || value_subtype != Variant::NIL) {
|
||||
String key_subtype_name = "Variant";
|
||||
if (key_subtype == Variant::OBJECT && (key_subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || key_subtype_hint == PROPERTY_HINT_NODE_TYPE)) {
|
||||
key_subtype_name = key_subtype_hint_string;
|
||||
} else if (key_subtype != Variant::NIL) {
|
||||
key_subtype_name = Variant::get_type_name(key_subtype);
|
||||
}
|
||||
String value_subtype_name = "Variant";
|
||||
if (value_subtype == Variant::OBJECT && (value_subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || value_subtype_hint == PROPERTY_HINT_NODE_TYPE)) {
|
||||
value_subtype_name = value_subtype_hint_string;
|
||||
} else if (value_subtype != Variant::NIL) {
|
||||
value_subtype_name = Variant::get_type_name(value_subtype);
|
||||
}
|
||||
if (preview_value) {
|
||||
dict_sub_type_name = vformat("[%s, %s] ", key_subtype_name, value_subtype_name);
|
||||
} else {
|
||||
dict_type_name += vformat("[%s, %s]", key_subtype_name, value_subtype_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (updated_val.get_type() != Variant::DICTIONARY) {
|
||||
edit->set_text(TTR("Dictionary (Nil)")); // This provides symmetry with the array property.
|
||||
if (preview_value) {
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
edit->set_button_icon(get_editor_theme_icon(SNAME("Nil")));
|
||||
edit->set_text(dict_type_name);
|
||||
} else {
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
edit->set_button_icon(Ref<Texture2D>());
|
||||
edit->set_text(vformat(TTR("(Nil) %s"), dict_type_name));
|
||||
}
|
||||
edit->set_pressed(false);
|
||||
if (container) {
|
||||
set_bottom_editor(nullptr);
|
||||
|
|
@ -996,7 +1178,25 @@ void EditorPropertyDictionary::update_property() {
|
|||
Dictionary dict = updated_val;
|
||||
object->set_dict(updated_val);
|
||||
|
||||
edit->set_text(vformat(TTR("Dictionary (size %d)"), dict.size()));
|
||||
if (preview_value) {
|
||||
String ctr_str = updated_val.get_construct_string().replace("\n", "");
|
||||
if (key_subtype != Variant::NIL || value_subtype != Variant::NIL) {
|
||||
int type_end = ctr_str.find("](");
|
||||
if (type_end > 0) {
|
||||
ctr_str = ctr_str.substr(type_end + 2).trim_suffix(")");
|
||||
}
|
||||
}
|
||||
|
||||
edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
edit->set_button_icon(get_editor_theme_icon(dict_type_name));
|
||||
edit->set_text(vformat("%s%s", dict_sub_type_name, ctr_str));
|
||||
edit->set_tooltip_text(vformat(TTR("%s%s (size %d)"), dict_type_name, dict_sub_type_name, dict.size()));
|
||||
} else {
|
||||
edit->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
edit->set_button_icon(Ref<Texture2D>());
|
||||
edit->set_text(vformat(TTR("%s (size %d)"), dict_type_name, dict.size()));
|
||||
}
|
||||
|
||||
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
|
||||
if (edit->is_pressed() != unfolded) {
|
||||
|
|
@ -1037,7 +1237,7 @@ void EditorPropertyDictionary::update_property() {
|
|||
_create_new_property_slot(EditorPropertyDictionaryObject::NEW_VALUE_INDEX);
|
||||
|
||||
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Key/Value Pair"));
|
||||
button_add_item->set_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
|
||||
button_add_item->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
|
||||
button_add_item->set_disabled(is_read_only());
|
||||
button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_add_key_value));
|
||||
add_vbox->add_child(button_add_item);
|
||||
|
|
@ -1062,6 +1262,44 @@ void EditorPropertyDictionary::update_property() {
|
|||
if (!slot_visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the editor property key needs to be updated.
|
||||
if (slot.prop_key) {
|
||||
Variant key;
|
||||
object->get_by_property_name(slot.key_name, key);
|
||||
Variant::Type key_type = key.get_type();
|
||||
|
||||
bool key_as_id = Object::cast_to<EncodedObjectAsID>(key);
|
||||
if (key_type != slot.key_type || (key_type == Variant::OBJECT && key_as_id != slot.key_as_id)) {
|
||||
slot.key_as_id = key_as_id;
|
||||
slot.key_type = key_type;
|
||||
EditorProperty *new_prop = nullptr;
|
||||
if (key_type == Variant::OBJECT && key_as_id) {
|
||||
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
|
||||
editor->setup("Object");
|
||||
new_prop = editor;
|
||||
} else {
|
||||
new_prop = EditorInspector::instantiate_property_editor(this, key_type, "", key_subtype_hint, key_subtype_hint_string, PROPERTY_USAGE_NONE);
|
||||
}
|
||||
new_prop->set_read_only(true);
|
||||
new_prop->set_selectable(false);
|
||||
new_prop->set_focus_mode(Control::FOCUS_NONE);
|
||||
new_prop->set_draw_background(false);
|
||||
new_prop->set_use_folding(is_using_folding());
|
||||
new_prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
new_prop->set_draw_label(false);
|
||||
EditorPropertyArray *arr_prop = Object::cast_to<EditorPropertyArray>(new_prop);
|
||||
if (arr_prop) {
|
||||
arr_prop->set_preview_value(true);
|
||||
}
|
||||
EditorPropertyDictionary *dict_prop = Object::cast_to<EditorPropertyDictionary>(new_prop);
|
||||
if (dict_prop) {
|
||||
dict_prop->set_preview_value(true);
|
||||
}
|
||||
slot.set_key_prop(new_prop);
|
||||
}
|
||||
}
|
||||
|
||||
Variant value;
|
||||
object->get_by_property_name(slot.prop_name, value);
|
||||
Variant::Type value_type = value.get_type();
|
||||
|
|
@ -1077,15 +1315,24 @@ void EditorPropertyDictionary::update_property() {
|
|||
editor->setup("Object");
|
||||
new_prop = editor;
|
||||
} else {
|
||||
new_prop = EditorInspector::instantiate_property_editor(this, value_type, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE);
|
||||
bool use_key = slot.index == EditorPropertyDictionaryObject::NEW_KEY_INDEX;
|
||||
new_prop = EditorInspector::instantiate_property_editor(this, value_type, "", use_key ? key_subtype_hint : value_subtype_hint,
|
||||
use_key ? key_subtype_hint_string : value_subtype_hint_string, PROPERTY_USAGE_NONE);
|
||||
}
|
||||
new_prop->set_selectable(false);
|
||||
new_prop->set_use_folding(is_using_folding());
|
||||
new_prop->connect(SNAME("property_changed"), callable_mp(this, &EditorPropertyDictionary::_property_changed));
|
||||
new_prop->connect(SNAME("object_id_selected"), callable_mp(this, &EditorPropertyDictionary::_object_id_selected));
|
||||
new_prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
if (slot.index != EditorPropertyDictionaryObject::NEW_KEY_INDEX && slot.index != EditorPropertyDictionaryObject::NEW_VALUE_INDEX) {
|
||||
new_prop->set_draw_label(false);
|
||||
}
|
||||
new_prop->set_read_only(is_read_only());
|
||||
slot.set_prop(new_prop);
|
||||
} else if (slot.index != EditorPropertyDictionaryObject::NEW_KEY_INDEX && slot.index != EditorPropertyDictionaryObject::NEW_VALUE_INDEX) {
|
||||
Variant key = dict.get_key_at_index(slot.index);
|
||||
String cs = key.get_construct_string();
|
||||
slot.prop->set_tooltip_text(cs);
|
||||
}
|
||||
|
||||
// We need to grab the focus of the property that is being changed, even if the type didn't actually changed.
|
||||
|
|
@ -1096,6 +1343,9 @@ void EditorPropertyDictionary::update_property() {
|
|||
}
|
||||
|
||||
slot.prop->update_property();
|
||||
if (slot.prop_key) {
|
||||
slot.prop_key->update_property();
|
||||
}
|
||||
}
|
||||
updating = false;
|
||||
|
||||
|
|
@ -1111,6 +1361,14 @@ void EditorPropertyDictionary::update_property() {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::_remove_pressed(int p_slot_index) {
|
||||
Dictionary dict = object->get_dict().duplicate();
|
||||
int index = slots[p_slot_index].index;
|
||||
dict.erase(dict.get_key_at_index(index));
|
||||
|
||||
emit_changed(get_edited_property(), dict);
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::_object_id_selected(const StringName &p_property, ObjectID p_id) {
|
||||
emit_signal(SNAME("object_id_selected"), p_property, p_id);
|
||||
}
|
||||
|
|
@ -1120,6 +1378,8 @@ void EditorPropertyDictionary::_notification(int p_what) {
|
|||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
change_type->clear();
|
||||
change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
|
||||
change_type->add_separator();
|
||||
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
||||
if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
|
||||
// These types can't be constructed or serialized properly, so skip them.
|
||||
|
|
@ -1129,11 +1389,9 @@ void EditorPropertyDictionary::_notification(int p_what) {
|
|||
String type = Variant::get_type_name(Variant::Type(i));
|
||||
change_type->add_icon_item(get_editor_theme_icon(type), type, i);
|
||||
}
|
||||
change_type->add_separator();
|
||||
change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
|
||||
|
||||
if (button_add_item) {
|
||||
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
add_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DictionaryAddItem")));
|
||||
}
|
||||
} break;
|
||||
|
|
@ -1143,7 +1401,7 @@ void EditorPropertyDictionary::_notification(int p_what) {
|
|||
void EditorPropertyDictionary::_edit_pressed() {
|
||||
Variant prop_val = get_edited_property_value();
|
||||
if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) {
|
||||
VariantInternal::initialize(&prop_val, Variant::DICTIONARY);
|
||||
initialize_dictionary(prop_val);
|
||||
emit_changed(get_edited_property(), prop_val);
|
||||
}
|
||||
|
||||
|
|
@ -1166,9 +1424,6 @@ void EditorPropertyDictionary::_page_changed(int p_page) {
|
|||
update_property();
|
||||
}
|
||||
|
||||
void EditorPropertyDictionary::_bind_methods() {
|
||||
}
|
||||
|
||||
bool EditorPropertyDictionary::is_colored(ColorationMode p_mode) {
|
||||
return p_mode == COLORATION_CONTAINER_RESOURCE;
|
||||
}
|
||||
|
|
@ -1191,8 +1446,16 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
|
|||
change_type = memnew(PopupMenu);
|
||||
add_child(change_type);
|
||||
change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyDictionary::_change_type_menu));
|
||||
changing_type_index = -1;
|
||||
changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE;
|
||||
has_borders = true;
|
||||
|
||||
key_subtype = Variant::NIL;
|
||||
key_subtype_hint = PROPERTY_HINT_NONE;
|
||||
key_subtype_hint_string = "";
|
||||
|
||||
value_subtype = Variant::NIL;
|
||||
value_subtype_hint = PROPERTY_HINT_NONE;
|
||||
value_subtype_hint_string = "";
|
||||
}
|
||||
|
||||
///////////////////// LOCALIZABLE STRING ///////////////////////////
|
||||
|
|
@ -1325,7 +1588,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_icon(get_editor_theme_icon(SNAME("Remove")));
|
||||
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));
|
||||
|
||||
|
|
@ -1334,7 +1597,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_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
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);
|
||||
}
|
||||
|
|
@ -1360,7 +1623,7 @@ void EditorPropertyLocalizableString::_notification(int p_what) {
|
|||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
if (button_add_item) {
|
||||
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
@ -1385,9 +1648,6 @@ void EditorPropertyLocalizableString::_page_changed(int p_page) {
|
|||
update_property();
|
||||
}
|
||||
|
||||
void EditorPropertyLocalizableString::_bind_methods() {
|
||||
}
|
||||
|
||||
EditorPropertyLocalizableString::EditorPropertyLocalizableString() {
|
||||
object.instantiate();
|
||||
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/editor_locale_dialog.h"
|
||||
#include "editor/filesystem_dock.h"
|
||||
|
||||
class Button;
|
||||
class EditorSpinSlider;
|
||||
|
|
@ -89,6 +88,7 @@ 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();
|
||||
};
|
||||
|
|
@ -115,6 +115,7 @@ class EditorPropertyArray : public EditorProperty {
|
|||
|
||||
PopupMenu *change_type = nullptr;
|
||||
|
||||
bool preview_value = false;
|
||||
int page_length = 20;
|
||||
int page_index = 0;
|
||||
int changing_type_index = EditorPropertyArrayObject::NOT_CHANGING_TYPE;
|
||||
|
|
@ -150,7 +151,6 @@ protected:
|
|||
bool updating = false;
|
||||
bool dropping = false;
|
||||
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
virtual void _add_element();
|
||||
|
|
@ -170,6 +170,7 @@ protected:
|
|||
|
||||
public:
|
||||
void setup(Variant::Type p_array_type, const String &p_hint_string = "");
|
||||
void set_preview_value(bool p_preview_value);
|
||||
virtual void update_property() override;
|
||||
virtual bool is_colored(ColorationMode p_mode) override;
|
||||
EditorPropertyArray();
|
||||
|
|
@ -183,13 +184,18 @@ class EditorPropertyDictionary : public EditorProperty {
|
|||
HBoxContainer *container = nullptr;
|
||||
int index = -1;
|
||||
Variant::Type type = Variant::VARIANT_MAX;
|
||||
Variant::Type key_type = Variant::VARIANT_MAX;
|
||||
bool as_id = false;
|
||||
bool key_as_id = false;
|
||||
EditorProperty *prop = nullptr;
|
||||
EditorProperty *prop_key = nullptr;
|
||||
String prop_name;
|
||||
String key_name;
|
||||
|
||||
void set_index(int p_idx) {
|
||||
index = p_idx;
|
||||
prop_name = object->get_property_name_for_index(p_idx);
|
||||
key_name = object->get_key_name_for_index(p_idx);
|
||||
update_prop_or_index();
|
||||
}
|
||||
|
||||
|
|
@ -200,15 +206,29 @@ class EditorPropertyDictionary : public EditorProperty {
|
|||
update_prop_or_index();
|
||||
}
|
||||
|
||||
void set_key_prop(EditorProperty *p_prop) {
|
||||
if (prop_key) {
|
||||
prop_key->add_sibling(p_prop);
|
||||
prop_key->queue_free();
|
||||
prop_key = p_prop;
|
||||
update_prop_or_index();
|
||||
}
|
||||
}
|
||||
|
||||
void update_prop_or_index() {
|
||||
prop->set_object_and_property(object.ptr(), prop_name);
|
||||
prop->set_label(object->get_label_for_index(index));
|
||||
if (prop_key) {
|
||||
prop_key->set_object_and_property(object.ptr(), key_name);
|
||||
} else {
|
||||
prop->set_label(object->get_label_for_index(index));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PopupMenu *change_type = nullptr;
|
||||
bool updating = false;
|
||||
|
||||
bool preview_value = false;
|
||||
Ref<EditorPropertyDictionaryObject> object;
|
||||
int page_length = 20;
|
||||
int page_index = 0;
|
||||
|
|
@ -220,7 +240,6 @@ class EditorPropertyDictionary : public EditorProperty {
|
|||
EditorSpinSlider *size_sliderv = nullptr;
|
||||
Button *button_add_item = nullptr;
|
||||
EditorPaginator *paginator = nullptr;
|
||||
PropertyHint property_hint;
|
||||
LocalVector<Slot> slots;
|
||||
void _create_new_property_slot(int p_idx);
|
||||
|
||||
|
|
@ -232,13 +251,22 @@ class EditorPropertyDictionary : public EditorProperty {
|
|||
|
||||
void _add_key_value();
|
||||
void _object_id_selected(const StringName &p_property, ObjectID p_id);
|
||||
void _remove_pressed(int p_slot_index);
|
||||
|
||||
Variant::Type key_subtype;
|
||||
PropertyHint key_subtype_hint;
|
||||
String key_subtype_hint_string;
|
||||
Variant::Type value_subtype;
|
||||
PropertyHint value_subtype_hint;
|
||||
String value_subtype_hint_string;
|
||||
void initialize_dictionary(Variant &p_dictionary);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void setup(PropertyHint p_hint);
|
||||
void setup(PropertyHint p_hint, const String &p_hint_string = "");
|
||||
void set_preview_value(bool p_preview_value);
|
||||
virtual void update_property() override;
|
||||
virtual bool is_colored(ColorationMode p_mode) override;
|
||||
EditorPropertyDictionary();
|
||||
|
|
@ -271,7 +299,6 @@ class EditorPropertyLocalizableString : public EditorProperty {
|
|||
void _object_id_selected(const StringName &p_property, ObjectID p_id);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -130,9 +130,11 @@ void EditorPropertyVectorN::_notification(int p_what) {
|
|||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
if (linked->is_visible()) {
|
||||
const String key = vformat("%s:%s", get_edited_object()->get_class(), get_edited_property());
|
||||
linked->set_pressed_no_signal(EditorSettings::get_singleton()->get_project_metadata("linked_properties", key, true));
|
||||
_update_ratio();
|
||||
if (get_edited_object()) {
|
||||
const String key = vformat("%s:%s", get_edited_object()->get_class(), get_edited_property());
|
||||
linked->set_pressed_no_signal(EditorSettings::get_singleton()->get_project_metadata("linked_properties", key, true));
|
||||
_update_ratio();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
|
|
@ -151,7 +153,7 @@ void EditorPropertyVectorN::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorPropertyVectorN::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix, bool p_radians_as_degrees) {
|
||||
void EditorPropertyVectorN::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix, bool p_radians_as_degrees, bool p_is_int) {
|
||||
radians_as_degrees = p_radians_as_degrees;
|
||||
|
||||
for (EditorSpinSlider *spin : spin_sliders) {
|
||||
|
|
@ -162,6 +164,7 @@ void EditorPropertyVectorN::setup(double p_min, double p_max, double p_step, boo
|
|||
spin->set_allow_greater(true);
|
||||
spin->set_allow_lesser(true);
|
||||
spin->set_suffix(p_suffix);
|
||||
spin->set_editing_integer(p_is_int);
|
||||
}
|
||||
|
||||
if (!p_link) {
|
||||
|
|
@ -236,7 +239,7 @@ EditorPropertyVectorN::EditorPropertyVectorN(Variant::Type p_type, bool p_force_
|
|||
linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
|
||||
linked->set_tooltip_text(TTR("Lock/Unlock Component Ratio"));
|
||||
linked->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyVectorN::_update_ratio));
|
||||
linked->connect(SNAME("toggled"), callable_mp(this, &EditorPropertyVectorN::_store_link));
|
||||
linked->connect(SceneStringName(toggled), callable_mp(this, &EditorPropertyVectorN::_store_link));
|
||||
hb->add_child(linked);
|
||||
|
||||
add_child(hb);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ protected:
|
|||
|
||||
public:
|
||||
virtual void update_property() override;
|
||||
void setup(double p_min, double p_max, double p_step = 1.0, bool p_hide_slider = true, bool p_link = false, const String &p_suffix = String(), bool p_radians_as_degrees = false);
|
||||
void setup(double p_min, double p_max, double p_step = 1.0, bool p_hide_slider = true, bool p_link = false, const String &p_suffix = String(), bool p_radians_as_degrees = false, bool p_is_int = false);
|
||||
EditorPropertyVectorN(Variant::Type p_type, bool p_force_wide, bool p_horizontal);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "editor_property_name_processor.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "editor_settings.h"
|
||||
|
||||
EditorPropertyNameProcessor *EditorPropertyNameProcessor::singleton = nullptr;
|
||||
|
|
@ -148,6 +148,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
|
|||
// https://github.com/godotengine/godot-editor-l10n/blob/main/scripts/common.py
|
||||
capitalize_string_remaps["2d"] = "2D";
|
||||
capitalize_string_remaps["3d"] = "3D";
|
||||
capitalize_string_remaps["4d"] = "4D";
|
||||
capitalize_string_remaps["aa"] = "AA";
|
||||
capitalize_string_remaps["aabb"] = "AABB";
|
||||
capitalize_string_remaps["adb"] = "ADB";
|
||||
|
|
@ -197,9 +198,11 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
|
|||
capitalize_string_remaps["gi"] = "GI";
|
||||
capitalize_string_remaps["gl"] = "GL";
|
||||
capitalize_string_remaps["glb"] = "GLB";
|
||||
capitalize_string_remaps["gles"] = "GLES";
|
||||
capitalize_string_remaps["gles2"] = "GLES2";
|
||||
capitalize_string_remaps["gles3"] = "GLES3";
|
||||
capitalize_string_remaps["gltf"] = "glTF";
|
||||
capitalize_string_remaps["gridmap"] = "GridMap";
|
||||
capitalize_string_remaps["gpu"] = "GPU";
|
||||
capitalize_string_remaps["gui"] = "GUI";
|
||||
capitalize_string_remaps["guid"] = "GUID";
|
||||
|
|
@ -230,8 +233,10 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
|
|||
capitalize_string_remaps["kb"] = "(KB)"; // Unit.
|
||||
capitalize_string_remaps["lcd"] = "LCD";
|
||||
capitalize_string_remaps["ldr"] = "LDR";
|
||||
capitalize_string_remaps["linuxbsd"] = "Linux/*BSD";
|
||||
capitalize_string_remaps["lod"] = "LOD";
|
||||
capitalize_string_remaps["lods"] = "LODs";
|
||||
capitalize_string_remaps["loongarch64"] = "loongarch64";
|
||||
capitalize_string_remaps["lowpass"] = "Low-pass";
|
||||
capitalize_string_remaps["macos"] = "macOS";
|
||||
capitalize_string_remaps["mb"] = "(MB)"; // Unit.
|
||||
|
|
@ -247,6 +252,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
|
|||
capitalize_string_remaps["oidn"] = "OIDN";
|
||||
capitalize_string_remaps["ok"] = "OK";
|
||||
capitalize_string_remaps["opengl"] = "OpenGL";
|
||||
capitalize_string_remaps["opengl3"] = "OpenGL 3";
|
||||
capitalize_string_remaps["opentype"] = "OpenType";
|
||||
capitalize_string_remaps["openxr"] = "OpenXR";
|
||||
capitalize_string_remaps["osslsigncode"] = "osslsigncode";
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class EditorPropertyNameProcessor : public Node {
|
|||
StringName _get_context(const String &p_name, const String &p_property, const StringName &p_class) const;
|
||||
|
||||
public:
|
||||
// Matches `interface/inspector/capitalize_properties` editor setting.
|
||||
// Matches `interface/inspector/default_property_name_style` editor setting.
|
||||
enum Style {
|
||||
STYLE_RAW,
|
||||
STYLE_CAPITALIZED,
|
||||
|
|
|
|||
|
|
@ -1,314 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* editor_quick_open.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "editor_quick_open.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
|
||||
Rect2i EditorQuickOpen::prev_rect = Rect2i();
|
||||
bool EditorQuickOpen::was_showed = false;
|
||||
|
||||
void EditorQuickOpen::popup_dialog(const String &p_base, bool p_enable_multi, bool p_dont_clear) {
|
||||
base_type = p_base;
|
||||
allow_multi_select = p_enable_multi;
|
||||
search_options->set_select_mode(allow_multi_select ? Tree::SELECT_MULTI : Tree::SELECT_SINGLE);
|
||||
|
||||
if (was_showed) {
|
||||
popup(prev_rect);
|
||||
} else {
|
||||
popup_centered_clamped(Size2(600, 440) * EDSCALE, 0.8f);
|
||||
}
|
||||
|
||||
EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->get_filesystem();
|
||||
_build_search_cache(efsd);
|
||||
|
||||
if (p_dont_clear) {
|
||||
search_box->select_all();
|
||||
_update_search();
|
||||
} else {
|
||||
search_box->clear(); // This will emit text_changed.
|
||||
}
|
||||
search_box->grab_focus();
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) {
|
||||
for (int i = 0; i < p_efsd->get_subdir_count(); i++) {
|
||||
_build_search_cache(p_efsd->get_subdir(i));
|
||||
}
|
||||
|
||||
Vector<String> base_types = base_type.split(",");
|
||||
for (int i = 0; i < p_efsd->get_file_count(); i++) {
|
||||
String file = p_efsd->get_file_path(i);
|
||||
String engine_type = p_efsd->get_file_type(i);
|
||||
String script_type = p_efsd->get_file_resource_script_class(i);
|
||||
String actual_type = script_type.is_empty() ? engine_type : script_type;
|
||||
|
||||
// Iterate all possible base types.
|
||||
for (String &parent_type : base_types) {
|
||||
if (ClassDB::is_parent_class(engine_type, parent_type) || EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)) {
|
||||
files.push_back(file.substr(6, file.length()));
|
||||
|
||||
// Store refs to used icons.
|
||||
String ext = file.get_extension();
|
||||
if (!icons.has(ext)) {
|
||||
icons.insert(ext, EditorNode::get_singleton()->get_class_icon(actual_type, "Object"));
|
||||
}
|
||||
|
||||
// Stop testing base types as soon as we got a match.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_update_search() {
|
||||
const PackedStringArray search_tokens = search_box->get_text().to_lower().replace("/", " ").split(" ", false);
|
||||
const bool empty_search = search_tokens.is_empty();
|
||||
|
||||
// Filter possible candidates.
|
||||
Vector<Entry> entries;
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
Entry r;
|
||||
r.path = files[i];
|
||||
if (empty_search) {
|
||||
entries.push_back(r);
|
||||
} else {
|
||||
r.score = _score_search_result(search_tokens, r.path.to_lower());
|
||||
if (r.score > 0) {
|
||||
entries.push_back(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display results
|
||||
TreeItem *root = search_options->get_root();
|
||||
root->clear_children();
|
||||
|
||||
if (entries.size() > 0) {
|
||||
if (!empty_search) {
|
||||
SortArray<Entry, EntryComparator> sorter;
|
||||
sorter.sort(entries.ptrw(), entries.size());
|
||||
}
|
||||
|
||||
const int class_icon_size = search_options->get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||
const int entry_limit = MIN(entries.size(), 300);
|
||||
for (int i = 0; i < entry_limit; i++) {
|
||||
TreeItem *ti = search_options->create_item(root);
|
||||
ti->set_text(0, entries[i].path);
|
||||
ti->set_icon_max_width(0, class_icon_size);
|
||||
ti->set_icon(0, *icons.lookup_ptr(entries[i].path.get_extension()));
|
||||
}
|
||||
|
||||
TreeItem *to_select = root->get_first_child();
|
||||
to_select->select(0);
|
||||
to_select->set_as_cursor(0);
|
||||
search_options->scroll_to_item(to_select);
|
||||
|
||||
get_ok_button()->set_disabled(false);
|
||||
} else {
|
||||
search_options->deselect_all();
|
||||
|
||||
get_ok_button()->set_disabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
float EditorQuickOpen::_score_search_result(const PackedStringArray &p_search_tokens, const String &p_path) {
|
||||
float score = 0.0f;
|
||||
int prev_min_match_idx = -1;
|
||||
|
||||
for (const String &s : p_search_tokens) {
|
||||
int min_match_idx = p_path.find(s);
|
||||
|
||||
if (min_match_idx == -1) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float token_score = s.length();
|
||||
|
||||
int max_match_idx = p_path.rfind(s);
|
||||
|
||||
// Prioritize the actual file name over folder.
|
||||
if (max_match_idx > p_path.rfind("/")) {
|
||||
token_score *= 2.0f;
|
||||
}
|
||||
|
||||
// Prioritize matches at the front of the path token.
|
||||
if (min_match_idx == 0 || p_path.contains("/" + s)) {
|
||||
token_score += 1.0f;
|
||||
}
|
||||
|
||||
score += token_score;
|
||||
|
||||
// Prioritize tokens which appear in order.
|
||||
if (prev_min_match_idx != -1 && max_match_idx > prev_min_match_idx) {
|
||||
score += 1.0f;
|
||||
}
|
||||
|
||||
prev_min_match_idx = min_match_idx;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_confirmed() {
|
||||
if (!search_options->get_selected()) {
|
||||
return;
|
||||
}
|
||||
_cleanup();
|
||||
hide();
|
||||
emit_signal(SNAME("quick_open"));
|
||||
}
|
||||
|
||||
void EditorQuickOpen::cancel_pressed() {
|
||||
_cleanup();
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_cleanup() {
|
||||
files.clear();
|
||||
icons.clear();
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_text_changed(const String &p_newtext) {
|
||||
_update_search();
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_sbox_input(const Ref<InputEvent> &p_ie) {
|
||||
Ref<InputEventKey> k = p_ie;
|
||||
if (k.is_valid()) {
|
||||
switch (k->get_keycode()) {
|
||||
case Key::UP:
|
||||
case Key::DOWN:
|
||||
case Key::PAGEUP:
|
||||
case Key::PAGEDOWN: {
|
||||
search_options->gui_input(k);
|
||||
search_box->accept_event();
|
||||
|
||||
if (allow_multi_select) {
|
||||
TreeItem *root = search_options->get_root();
|
||||
if (!root->get_first_child()) {
|
||||
break;
|
||||
}
|
||||
|
||||
TreeItem *current = search_options->get_selected();
|
||||
TreeItem *item = search_options->get_next_selected(root);
|
||||
while (item) {
|
||||
item->deselect(0);
|
||||
item = search_options->get_next_selected(item);
|
||||
}
|
||||
|
||||
current->select(0);
|
||||
current->set_as_cursor(0);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String EditorQuickOpen::get_selected() const {
|
||||
TreeItem *ti = search_options->get_selected();
|
||||
if (!ti) {
|
||||
return String();
|
||||
}
|
||||
|
||||
return "res://" + ti->get_text(0);
|
||||
}
|
||||
|
||||
Vector<String> EditorQuickOpen::get_selected_files() const {
|
||||
Vector<String> selected_files;
|
||||
|
||||
TreeItem *item = search_options->get_next_selected(search_options->get_root());
|
||||
while (item) {
|
||||
selected_files.push_back("res://" + item->get_text(0));
|
||||
item = search_options->get_next_selected(item);
|
||||
}
|
||||
|
||||
return selected_files;
|
||||
}
|
||||
|
||||
String EditorQuickOpen::get_base_type() const {
|
||||
return base_type;
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
connect(SceneStringName(confirmed), callable_mp(this, &EditorQuickOpen::_confirmed));
|
||||
|
||||
search_box->set_clear_button_enabled(true);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible()) {
|
||||
prev_rect = Rect2i(get_position(), get_size());
|
||||
was_showed = true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
disconnect(SceneStringName(confirmed), callable_mp(this, &EditorQuickOpen::_confirmed));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorQuickOpen::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("quick_open"));
|
||||
}
|
||||
|
||||
EditorQuickOpen::EditorQuickOpen() {
|
||||
VBoxContainer *vbc = memnew(VBoxContainer);
|
||||
add_child(vbc);
|
||||
|
||||
search_box = memnew(LineEdit);
|
||||
search_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorQuickOpen::_text_changed));
|
||||
search_box->connect(SceneStringName(gui_input), callable_mp(this, &EditorQuickOpen::_sbox_input));
|
||||
vbc->add_margin_child(TTR("Search:"), search_box);
|
||||
register_text_enter(search_box);
|
||||
|
||||
search_options = memnew(Tree);
|
||||
search_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
search_options->connect("item_activated", callable_mp(this, &EditorQuickOpen::_confirmed));
|
||||
search_options->create_item();
|
||||
search_options->set_hide_root(true);
|
||||
search_options->set_hide_folding(true);
|
||||
search_options->add_theme_constant_override("draw_guides", 1);
|
||||
vbc->add_margin_child(TTR("Matches:"), search_options, true);
|
||||
|
||||
set_ok_button_text(TTR("Open"));
|
||||
set_hide_on_ok(false);
|
||||
}
|
||||
|
|
@ -33,12 +33,12 @@
|
|||
#include "editor/audio_stream_preview.h"
|
||||
#include "editor/editor_help.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_quick_open.h"
|
||||
#include "editor/editor_resource_preview.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/filesystem_dock.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/gui/editor_quick_open_dialog.h"
|
||||
#include "editor/plugins/editor_resource_conversion_plugin.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/scene_tree_dock.h"
|
||||
|
|
@ -61,11 +61,11 @@ void EditorResourcePicker::_update_resource() {
|
|||
assign_button->set_custom_minimum_size(assign_button_min_size);
|
||||
|
||||
if (edited_resource == Ref<Resource>()) {
|
||||
assign_button->set_icon(Ref<Texture2D>());
|
||||
assign_button->set_button_icon(Ref<Texture2D>());
|
||||
assign_button->set_text(TTR("<empty>"));
|
||||
assign_button->set_tooltip_text("");
|
||||
} else {
|
||||
assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), SNAME("Object")));
|
||||
assign_button->set_button_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), SNAME("Object")));
|
||||
|
||||
if (!edited_resource->get_name().is_empty()) {
|
||||
assign_button->set_text(edited_resource->get_name());
|
||||
|
|
@ -87,11 +87,11 @@ void EditorResourcePicker::_update_resource() {
|
|||
assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + edited_resource->get_class());
|
||||
}
|
||||
|
||||
assign_button->set_disabled(!editable && !edited_resource.is_valid());
|
||||
assign_button->set_disabled(!editable && edited_resource.is_null());
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj) {
|
||||
if (!edited_resource.is_valid() || edited_resource->get_instance_id() != p_obj) {
|
||||
if (edited_resource.is_null() || edited_resource->get_instance_id() != p_obj) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ void EditorResourcePicker::_update_resource_preview(const String &p_path, const
|
|||
}
|
||||
|
||||
if (p_preview.is_valid()) {
|
||||
preview_rect->set_offset(SIDE_LEFT, assign_button->get_icon()->get_width() + assign_button->get_theme_stylebox(CoreStringName(normal))->get_content_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button")));
|
||||
preview_rect->set_offset(SIDE_LEFT, assign_button->get_button_icon()->get_width() + assign_button->get_theme_stylebox(CoreStringName(normal))->get_content_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button")));
|
||||
|
||||
// Resource-specific stretching.
|
||||
if (Ref<GradientTexture1D>(edited_resource).is_valid() || Ref<Gradient>(edited_resource).is_valid()) {
|
||||
|
|
@ -132,6 +132,11 @@ void EditorResourcePicker::_resource_selected() {
|
|||
emit_signal(SNAME("resource_selected"), edited_resource, false);
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_resource_changed() {
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_file_selected(const String &p_path) {
|
||||
Ref<Resource> loaded_resource = ResourceLoader::load(p_path);
|
||||
ERR_FAIL_COND_MSG(loaded_resource.is_null(), "Cannot load resource from path '" + p_path + "'.");
|
||||
|
|
@ -167,12 +172,14 @@ void EditorResourcePicker::_file_selected(const String &p_path) {
|
|||
}
|
||||
|
||||
edited_resource = loaded_resource;
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_file_quick_selected() {
|
||||
_file_selected(quick_open->get_selected());
|
||||
void EditorResourcePicker::_resource_saved(Object *p_resource) {
|
||||
if (edited_resource.is_valid() && p_resource == edited_resource.ptr()) {
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorResourcePicker::_update_menu() {
|
||||
|
|
@ -217,7 +224,9 @@ void EditorResourcePicker::_update_menu_items() {
|
|||
}
|
||||
|
||||
if (is_editable()) {
|
||||
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR);
|
||||
if (!_is_custom_type_script()) {
|
||||
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR);
|
||||
}
|
||||
edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
|
||||
|
||||
// Check whether the resource has subresources.
|
||||
|
|
@ -279,20 +288,22 @@ void EditorResourcePicker::_update_menu_items() {
|
|||
|
||||
// Add options to convert existing resource to another type of resource.
|
||||
if (is_editable() && edited_resource.is_valid()) {
|
||||
Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(edited_resource);
|
||||
if (conversions.size()) {
|
||||
Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin_for_resource(edited_resource);
|
||||
if (!conversions.is_empty()) {
|
||||
edit_menu->add_separator();
|
||||
}
|
||||
for (int i = 0; i < conversions.size(); i++) {
|
||||
String what = conversions[i]->converts_to();
|
||||
int relative_id = 0;
|
||||
for (const Ref<EditorResourceConversionPlugin> &conversion : conversions) {
|
||||
String what = conversion->converts_to();
|
||||
Ref<Texture2D> icon;
|
||||
if (has_theme_icon(what, EditorStringName(EditorIcons))) {
|
||||
icon = get_editor_theme_icon(what);
|
||||
} else {
|
||||
icon = get_theme_icon(what, SNAME("Resource"));
|
||||
icon = get_editor_theme_icon(SNAME("Object"));
|
||||
}
|
||||
|
||||
edit_menu->add_icon_item(icon, vformat(TTR("Convert to %s"), what), CONVERT_BASE_ID + i);
|
||||
edit_menu->add_icon_item(icon, vformat(TTR("Convert to %s"), what), CONVERT_BASE_ID + relative_id);
|
||||
relative_id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -330,14 +341,14 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
} break;
|
||||
|
||||
case OBJ_MENU_QUICKLOAD: {
|
||||
if (!quick_open) {
|
||||
quick_open = memnew(EditorQuickOpen);
|
||||
add_child(quick_open);
|
||||
quick_open->connect("quick_open", callable_mp(this, &EditorResourcePicker::_file_quick_selected));
|
||||
const Vector<String> &base_types_string = base_type.split(",");
|
||||
|
||||
Vector<StringName> base_types;
|
||||
for (const String &type : base_types_string) {
|
||||
base_types.push_back(type);
|
||||
}
|
||||
|
||||
quick_open->popup_dialog(base_type);
|
||||
quick_open->set_title(TTR("Resource"));
|
||||
EditorNode::get_singleton()->get_quick_open_dialog()->popup_dialog(base_types, callable_mp(this, &EditorResourcePicker::_file_selected));
|
||||
} break;
|
||||
|
||||
case OBJ_MENU_INSPECT: {
|
||||
|
|
@ -348,8 +359,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
|
||||
case OBJ_MENU_CLEAR: {
|
||||
edited_resource = Ref<Resource>();
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
} break;
|
||||
|
||||
case OBJ_MENU_MAKE_UNIQUE: {
|
||||
|
|
@ -361,8 +371,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
ERR_FAIL_COND(unique_resource.is_null()); // duplicate() may fail.
|
||||
|
||||
edited_resource = unique_resource;
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
} break;
|
||||
|
||||
case OBJ_MENU_MAKE_UNIQUE_RECURSIVE: {
|
||||
|
|
@ -408,6 +417,10 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
if (edited_resource.is_null()) {
|
||||
return;
|
||||
}
|
||||
Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved);
|
||||
if (!EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) {
|
||||
EditorNode::get_singleton()->connect("resource_saved", resource_saved);
|
||||
}
|
||||
EditorNode::get_singleton()->save_resource_as(edited_resource);
|
||||
} break;
|
||||
|
||||
|
|
@ -423,9 +436,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
_edit_menu_cbk(OBJ_MENU_MAKE_UNIQUE);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
} break;
|
||||
|
||||
case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
|
||||
|
|
@ -440,12 +451,11 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
|
||||
if (p_which >= CONVERT_BASE_ID) {
|
||||
int to_type = p_which - CONVERT_BASE_ID;
|
||||
Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(edited_resource);
|
||||
Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin_for_resource(edited_resource);
|
||||
ERR_FAIL_INDEX(to_type, conversions.size());
|
||||
|
||||
edited_resource = conversions[to_type]->convert(edited_resource);
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -472,8 +482,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
|
|||
// Prevent freeing of the object until the end of the update of the resource (GH-88286).
|
||||
Ref<Resource> old_edited_resource = edited_resource;
|
||||
edited_resource = Ref<Resource>(resp);
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -569,9 +578,9 @@ String EditorResourcePicker::_get_resource_type(const Ref<Resource> &p_resource)
|
|||
return res_type;
|
||||
}
|
||||
|
||||
static void _add_allowed_type(const StringName &p_type, HashSet<StringName> *p_vector) {
|
||||
if (p_vector->has(p_type)) {
|
||||
// Already added
|
||||
static void _add_allowed_type(const StringName &p_type, List<StringName> *p_vector) {
|
||||
if (p_vector->find(p_type)) {
|
||||
// Already added.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -579,7 +588,7 @@ static void _add_allowed_type(const StringName &p_type, HashSet<StringName> *p_v
|
|||
// Engine class,
|
||||
|
||||
if (!ClassDB::is_virtual(p_type)) {
|
||||
p_vector->insert(p_type);
|
||||
p_vector->push_back(p_type);
|
||||
}
|
||||
|
||||
List<StringName> inheriters;
|
||||
|
|
@ -589,7 +598,7 @@ static void _add_allowed_type(const StringName &p_type, HashSet<StringName> *p_v
|
|||
}
|
||||
} else {
|
||||
// Script class.
|
||||
p_vector->insert(p_type);
|
||||
p_vector->push_back(p_type);
|
||||
}
|
||||
|
||||
List<StringName> inheriters;
|
||||
|
|
@ -604,12 +613,22 @@ void EditorResourcePicker::_ensure_allowed_types() const {
|
|||
return;
|
||||
}
|
||||
|
||||
List<StringName> final_allowed;
|
||||
|
||||
Vector<String> allowed_types = base_type.split(",");
|
||||
int size = allowed_types.size();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
const String base = allowed_types[i].strip_edges();
|
||||
_add_allowed_type(base, &allowed_types_without_convert);
|
||||
for (const String &S : allowed_types) {
|
||||
const String base = S.strip_edges();
|
||||
if (base.begins_with("-")) {
|
||||
final_allowed.erase(base.right(-1));
|
||||
continue;
|
||||
}
|
||||
_add_allowed_type(base, &final_allowed);
|
||||
}
|
||||
|
||||
for (const StringName &SN : final_allowed) {
|
||||
allowed_types_without_convert.insert(SN);
|
||||
}
|
||||
|
||||
allowed_types_with_convert = HashSet<StringName>(allowed_types_without_convert);
|
||||
|
|
@ -618,9 +637,9 @@ void EditorResourcePicker::_ensure_allowed_types() const {
|
|||
const String base = allowed_types[i].strip_edges();
|
||||
if (base == "BaseMaterial3D") {
|
||||
allowed_types_with_convert.insert("Texture2D");
|
||||
} else if (base == "ShaderMaterial") {
|
||||
} else if (ClassDB::is_parent_class("ShaderMaterial", base)) {
|
||||
allowed_types_with_convert.insert("Shader");
|
||||
} else if (base == "Texture2D") {
|
||||
} else if (ClassDB::is_parent_class("ImageTexture", base)) {
|
||||
allowed_types_with_convert.insert("Image");
|
||||
}
|
||||
}
|
||||
|
|
@ -687,6 +706,16 @@ bool EditorResourcePicker::_is_type_valid(const String &p_type_name, const HashS
|
|||
return false;
|
||||
}
|
||||
|
||||
bool EditorResourcePicker::_is_custom_type_script() const {
|
||||
Ref<Script> resource_as_script = edited_resource;
|
||||
|
||||
if (resource_as_script.is_valid() && resource_owner && resource_owner->has_meta(SceneStringName(_custom_type_script))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Variant EditorResourcePicker::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
|
||||
if (edited_resource.is_valid()) {
|
||||
Dictionary drag_data = EditorNode::get_singleton()->drag_resource(edited_resource, p_from);
|
||||
|
|
@ -716,7 +745,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
dropped_resource = drag_data["resource"];
|
||||
}
|
||||
|
||||
if (!dropped_resource.is_valid() && drag_data.has("type") && String(drag_data["type"]) == "files") {
|
||||
if (dropped_resource.is_null() && drag_data.has("type") && String(drag_data["type"]) == "files") {
|
||||
Vector<String> files = drag_data["files"];
|
||||
|
||||
if (files.size() == 1) {
|
||||
|
|
@ -738,7 +767,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
if (at == "BaseMaterial3D" && Ref<Texture2D>(dropped_resource).is_valid()) {
|
||||
// Use existing resource if possible and only replace its data.
|
||||
Ref<StandardMaterial3D> mat = edited_resource;
|
||||
if (!mat.is_valid()) {
|
||||
if (mat.is_null()) {
|
||||
mat.instantiate();
|
||||
}
|
||||
mat->set_texture(StandardMaterial3D::TextureParam::TEXTURE_ALBEDO, dropped_resource);
|
||||
|
|
@ -748,7 +777,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
|
||||
if (at == "ShaderMaterial" && Ref<Shader>(dropped_resource).is_valid()) {
|
||||
Ref<ShaderMaterial> mat = edited_resource;
|
||||
if (!mat.is_valid()) {
|
||||
if (mat.is_null()) {
|
||||
mat.instantiate();
|
||||
}
|
||||
mat->set_shader(dropped_resource);
|
||||
|
|
@ -758,7 +787,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
|
||||
if (at == "ImageTexture" && Ref<Image>(dropped_resource).is_valid()) {
|
||||
Ref<ImageTexture> texture = edited_resource;
|
||||
if (!texture.is_valid()) {
|
||||
if (texture.is_null()) {
|
||||
texture.instantiate();
|
||||
}
|
||||
texture->set_image(dropped_resource);
|
||||
|
|
@ -769,8 +798,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
|
|||
}
|
||||
|
||||
edited_resource = dropped_resource;
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -813,7 +841,7 @@ void EditorResourcePicker::_notification(int p_what) {
|
|||
edit_menu->add_theme_constant_override("icon_max_width", icon_width);
|
||||
}
|
||||
|
||||
edit_button->set_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree")));
|
||||
edit_button->set_button_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
|
|
@ -833,6 +861,13 @@ void EditorResourcePicker::_notification(int p_what) {
|
|||
assign_button->queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved);
|
||||
if (EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) {
|
||||
EditorNode::get_singleton()->disconnect("resource_saved", resource_saved);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -886,7 +921,7 @@ Vector<String> EditorResourcePicker::get_allowed_types() const {
|
|||
}
|
||||
|
||||
void EditorResourcePicker::set_edited_resource(Ref<Resource> p_resource) {
|
||||
if (!p_resource.is_valid()) {
|
||||
if (p_resource.is_null()) {
|
||||
edited_resource = Ref<Resource>();
|
||||
_update_resource();
|
||||
return;
|
||||
|
|
@ -936,9 +971,17 @@ void EditorResourcePicker::set_toggle_pressed(bool p_pressed) {
|
|||
assign_button->set_pressed(p_pressed);
|
||||
}
|
||||
|
||||
bool EditorResourcePicker::is_toggle_pressed() const {
|
||||
return assign_button->is_pressed();
|
||||
}
|
||||
|
||||
void EditorResourcePicker::set_resource_owner(Object *p_object) {
|
||||
resource_owner = p_object;
|
||||
}
|
||||
|
||||
void EditorResourcePicker::set_editable(bool p_editable) {
|
||||
editable = p_editable;
|
||||
assign_button->set_disabled(!editable && !edited_resource.is_valid());
|
||||
assign_button->set_disabled(!editable && edited_resource.is_null());
|
||||
edit_button->set_visible(editable);
|
||||
}
|
||||
|
||||
|
|
@ -1030,8 +1073,7 @@ void EditorResourcePicker::_duplicate_selected_resources() {
|
|||
|
||||
if (meta.size() == 1) { // Root.
|
||||
edited_resource = unique_resource;
|
||||
emit_signal(SNAME("resource_changed"), edited_resource);
|
||||
_update_resource();
|
||||
_resource_changed();
|
||||
} else {
|
||||
Array parent_meta = item->get_parent()->get_metadata(0);
|
||||
Ref<Resource> parent = parent_meta[0];
|
||||
|
|
@ -1067,6 +1109,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
|
|||
edit_button = memnew(Button);
|
||||
edit_button->set_flat(false);
|
||||
edit_button->set_toggle_mode(true);
|
||||
edit_button->set_action_mode(BaseButton::ACTION_MODE_BUTTON_PRESS);
|
||||
edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorResourcePicker::_update_menu));
|
||||
add_child(edit_button);
|
||||
edit_button->connect(SceneStringName(gui_input), callable_mp(this, &EditorResourcePicker::_button_input));
|
||||
|
|
@ -1082,7 +1125,10 @@ void EditorScriptPicker::set_create_options(Object *p_menu_node) {
|
|||
return;
|
||||
}
|
||||
|
||||
menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT);
|
||||
if (!(script_owner && script_owner->has_meta(SceneStringName(_custom_type_script)))) {
|
||||
menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT);
|
||||
}
|
||||
|
||||
if (script_owner) {
|
||||
Ref<Script> scr = script_owner->get_script();
|
||||
if (scr.is_valid()) {
|
||||
|
|
@ -1251,7 +1297,7 @@ void EditorAudioStreamPicker::_update_resource() {
|
|||
|
||||
void EditorAudioStreamPicker::_preview_draw() {
|
||||
Ref<AudioStream> audio_stream = get_edited_resource();
|
||||
if (!audio_stream.is_valid()) {
|
||||
if (audio_stream.is_null()) {
|
||||
get_assign_button()->set_text(TTR("<empty>"));
|
||||
return;
|
||||
}
|
||||
|
|
@ -1324,7 +1370,7 @@ void EditorAudioStreamPicker::_preview_draw() {
|
|||
}
|
||||
|
||||
stream_preview_rect->draw_texture(icon, Point2i(EDSCALE * 4, rect.position.y + (rect.size.height - icon->get_height()) / 2), icon_modulate);
|
||||
stream_preview_rect->draw_string(font, Point2i(EDSCALE * 4 + icon->get_width(), rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, size.width - 4 * EDSCALE - icon->get_width(), font_size, get_theme_color(SNAME("font_color"), EditorStringName(Editor)));
|
||||
stream_preview_rect->draw_string(font, Point2i(EDSCALE * 4 + icon->get_width(), rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, size.width - 4 * EDSCALE - icon->get_width(), font_size, get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
|
||||
}
|
||||
|
||||
EditorAudioStreamPicker::EditorAudioStreamPicker() :
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
class Button;
|
||||
class ConfirmationDialog;
|
||||
class EditorFileDialog;
|
||||
class EditorQuickOpen;
|
||||
class PopupMenu;
|
||||
class TextureRect;
|
||||
class Tree;
|
||||
|
|
@ -59,7 +58,6 @@ class EditorResourcePicker : public HBoxContainer {
|
|||
TextureRect *preview_rect = nullptr;
|
||||
Button *edit_button = nullptr;
|
||||
EditorFileDialog *file_dialog = nullptr;
|
||||
EditorQuickOpen *quick_open = nullptr;
|
||||
|
||||
ConfirmationDialog *duplicate_resources_dialog = nullptr;
|
||||
Tree *duplicate_resources_tree = nullptr;
|
||||
|
|
@ -83,14 +81,18 @@ class EditorResourcePicker : public HBoxContainer {
|
|||
CONVERT_BASE_ID = 1000,
|
||||
};
|
||||
|
||||
Object *resource_owner = nullptr;
|
||||
|
||||
PopupMenu *edit_menu = nullptr;
|
||||
|
||||
void _update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj);
|
||||
|
||||
void _resource_selected();
|
||||
void _file_quick_selected();
|
||||
void _resource_changed();
|
||||
void _file_selected(const String &p_path);
|
||||
|
||||
void _resource_saved(Object *p_resource);
|
||||
|
||||
void _update_menu();
|
||||
void _update_menu_items();
|
||||
void _edit_menu_cbk(int p_which);
|
||||
|
|
@ -102,6 +104,7 @@ class EditorResourcePicker : public HBoxContainer {
|
|||
void _ensure_allowed_types() const;
|
||||
bool _is_drop_valid(const Dictionary &p_drag_data) const;
|
||||
bool _is_type_valid(const String &p_type_name, const HashSet<StringName> &p_allowed_types) const;
|
||||
bool _is_custom_type_script() const;
|
||||
|
||||
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
|
||||
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
|
|
@ -135,6 +138,9 @@ public:
|
|||
void set_toggle_mode(bool p_enable);
|
||||
bool is_toggle_mode() const;
|
||||
void set_toggle_pressed(bool p_pressed);
|
||||
bool is_toggle_pressed() const;
|
||||
|
||||
void set_resource_owner(Object *p_object);
|
||||
|
||||
void set_editable(bool p_editable);
|
||||
bool is_editable() const;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
#include "servers/rendering/rendering_server_default.h"
|
||||
#include "servers/rendering/rendering_server_globals.h"
|
||||
|
||||
bool EditorResourcePreviewGenerator::handles(const String &p_type) const {
|
||||
bool success = false;
|
||||
|
|
@ -67,7 +67,7 @@ Ref<Texture2D> EditorResourcePreviewGenerator::generate_from_path(const String &
|
|||
}
|
||||
|
||||
Ref<Resource> res = ResourceLoader::load(p_path);
|
||||
if (!res.is_valid()) {
|
||||
if (res.is_null()) {
|
||||
return res;
|
||||
}
|
||||
return generate(res, p_size, p_metadata);
|
||||
|
|
@ -96,12 +96,12 @@ void EditorResourcePreviewGenerator::_bind_methods() {
|
|||
EditorResourcePreviewGenerator::EditorResourcePreviewGenerator() {
|
||||
}
|
||||
|
||||
void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewport) const {
|
||||
void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewport) {
|
||||
Callable request_vp_update_once = callable_mp(RS::get_singleton(), &RS::viewport_set_update_mode).bind(p_viewport, RS::VIEWPORT_UPDATE_ONCE);
|
||||
|
||||
if (EditorResourcePreview::get_singleton()->is_threaded()) {
|
||||
RS::get_singleton()->connect(SNAME("frame_pre_draw"), request_vp_update_once, Object::CONNECT_ONE_SHOT);
|
||||
RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorResourcePreviewGenerator::DrawRequester *>(this), &EditorResourcePreviewGenerator::DrawRequester::_post_semaphore));
|
||||
RS::get_singleton()->request_frame_drawn_callback(callable_mp(this, &EditorResourcePreviewGenerator::DrawRequester::_post_semaphore));
|
||||
|
||||
semaphore.wait();
|
||||
} else {
|
||||
|
|
@ -119,19 +119,19 @@ void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewp
|
|||
}
|
||||
}
|
||||
|
||||
void EditorResourcePreviewGenerator::DrawRequester::abort() const {
|
||||
void EditorResourcePreviewGenerator::DrawRequester::abort() {
|
||||
if (EditorResourcePreview::get_singleton()->is_threaded()) {
|
||||
semaphore.post();
|
||||
}
|
||||
}
|
||||
|
||||
Variant EditorResourcePreviewGenerator::DrawRequester::_post_semaphore() const {
|
||||
Variant EditorResourcePreviewGenerator::DrawRequester::_post_semaphore() {
|
||||
semaphore.post();
|
||||
return Variant(); // Needed because of how the callback is used.
|
||||
}
|
||||
|
||||
bool EditorResourcePreview::is_threaded() const {
|
||||
return RSG::texture_storage->can_create_resources_async();
|
||||
return RSG::rasterizer->can_create_resources_async();
|
||||
}
|
||||
|
||||
void EditorResourcePreview::_thread_func(void *ud) {
|
||||
|
|
@ -147,6 +147,10 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con
|
|||
|
||||
if (!p_path.begins_with("ID:")) {
|
||||
modified_time = FileAccess::get_modified_time(p_path);
|
||||
String import_path = p_path + ".import";
|
||||
if (FileAccess::exists(import_path)) {
|
||||
modified_time = MAX(modified_time, FileAccess::get_modified_time(import_path));
|
||||
}
|
||||
}
|
||||
|
||||
Item item;
|
||||
|
|
@ -213,7 +217,7 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
|
|||
r_small_texture = generated_small;
|
||||
}
|
||||
|
||||
if (!r_small_texture.is_valid() && r_texture.is_valid() && preview_generators[i]->generate_small_preview_automatically()) {
|
||||
if (r_small_texture.is_null() && r_texture.is_valid() && preview_generators[i]->generate_small_preview_automatically()) {
|
||||
Ref<Image> small_image = r_texture->get_image();
|
||||
small_image = small_image->duplicate();
|
||||
small_image->resize(small_thumbnail_size, small_thumbnail_size, Image::INTERPOLATE_CUBIC);
|
||||
|
|
@ -221,10 +225,12 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
|
|||
r_small_texture->set_image(small_image);
|
||||
}
|
||||
|
||||
break;
|
||||
if (generated.is_valid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_item.resource.is_valid()) {
|
||||
if (p_item.resource.is_null()) {
|
||||
// Cache the preview in case it's a resource on disk.
|
||||
if (r_texture.is_valid()) {
|
||||
// Wow it generated a preview... save cache.
|
||||
|
|
@ -235,7 +241,14 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref<
|
|||
}
|
||||
Ref<FileAccess> f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE);
|
||||
ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions.");
|
||||
_write_preview_cache(f, thumbnail_size, has_small_texture, FileAccess::get_modified_time(p_item.path), FileAccess::get_md5(p_item.path), p_metadata);
|
||||
|
||||
uint64_t modtime = FileAccess::get_modified_time(p_item.path);
|
||||
String import_path = p_item.path + ".import";
|
||||
if (FileAccess::exists(import_path)) {
|
||||
modtime = MAX(modtime, FileAccess::get_modified_time(import_path));
|
||||
}
|
||||
|
||||
_write_preview_cache(f, thumbnail_size, has_small_texture, modtime, FileAccess::get_md5(p_item.path), p_metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -296,6 +309,11 @@ void EditorResourcePreview::_iterate() {
|
|||
_generate_preview(texture, small_texture, item, cache_base, preview_metadata);
|
||||
} else {
|
||||
uint64_t modtime = FileAccess::get_modified_time(item.path);
|
||||
String import_path = item.path + ".import";
|
||||
if (FileAccess::exists(import_path)) {
|
||||
modtime = MAX(modtime, FileAccess::get_modified_time(import_path));
|
||||
}
|
||||
|
||||
int tsize;
|
||||
bool has_small_texture;
|
||||
uint64_t last_modtime;
|
||||
|
|
@ -412,9 +430,27 @@ void EditorResourcePreview::_update_thumbnail_sizes() {
|
|||
}
|
||||
}
|
||||
|
||||
EditorResourcePreview::PreviewItem EditorResourcePreview::get_resource_preview_if_available(const String &p_path) {
|
||||
PreviewItem item;
|
||||
{
|
||||
MutexLock lock(preview_mutex);
|
||||
|
||||
HashMap<String, EditorResourcePreview::Item>::Iterator I = cache.find(p_path);
|
||||
if (!I) {
|
||||
return item;
|
||||
}
|
||||
|
||||
EditorResourcePreview::Item &cached_item = I->value;
|
||||
item.preview = cached_item.preview;
|
||||
item.small_preview = cached_item.small_preview;
|
||||
}
|
||||
preview_sem.post();
|
||||
return item;
|
||||
}
|
||||
|
||||
void EditorResourcePreview::queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata) {
|
||||
ERR_FAIL_NULL(p_receiver);
|
||||
ERR_FAIL_COND(!p_res.is_valid());
|
||||
ERR_FAIL_COND(p_res.is_null());
|
||||
_update_thumbnail_sizes();
|
||||
|
||||
{
|
||||
|
|
@ -486,6 +522,14 @@ void EditorResourcePreview::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("preview_invalidated", PropertyInfo(Variant::STRING, "path")));
|
||||
}
|
||||
|
||||
void EditorResourcePreview::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
stop();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorResourcePreview::check_for_invalidation(const String &p_path) {
|
||||
bool call_invalidated = false;
|
||||
{
|
||||
|
|
@ -493,6 +537,11 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) {
|
|||
|
||||
if (cache.has(p_path)) {
|
||||
uint64_t modified_time = FileAccess::get_modified_time(p_path);
|
||||
String import_path = p_path + ".import";
|
||||
if (FileAccess::exists(import_path)) {
|
||||
modified_time = MAX(modified_time, FileAccess::get_modified_time(import_path));
|
||||
}
|
||||
|
||||
if (modified_time != cache[p_path].modified_time) {
|
||||
cache.erase(p_path);
|
||||
call_invalidated = true;
|
||||
|
|
@ -531,8 +580,10 @@ void EditorResourcePreview::stop() {
|
|||
}
|
||||
|
||||
while (!exited.is_set()) {
|
||||
// Sync pending work.
|
||||
OS::get_singleton()->delay_usec(10000);
|
||||
RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on rendering server
|
||||
RenderingServer::get_singleton()->sync();
|
||||
MessageQueue::get_singleton()->flush();
|
||||
}
|
||||
|
||||
thread.wait_to_finish();
|
||||
|
|
|
|||
|
|
@ -54,18 +54,18 @@ protected:
|
|||
class DrawRequester : public Object {
|
||||
Semaphore semaphore;
|
||||
|
||||
Variant _post_semaphore() const;
|
||||
Variant _post_semaphore();
|
||||
|
||||
public:
|
||||
void request_and_wait(RID p_viewport) const;
|
||||
void abort() const;
|
||||
void request_and_wait(RID p_viewport);
|
||||
void abort();
|
||||
};
|
||||
|
||||
public:
|
||||
virtual bool handles(const String &p_type) const;
|
||||
virtual Ref<Texture2D> generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const;
|
||||
virtual Ref<Texture2D> generate_from_path(const String &p_path, const Size2 &p_size, Dictionary &p_metadata) const;
|
||||
virtual void abort(){};
|
||||
virtual void abort() {}
|
||||
|
||||
virtual bool generate_small_preview_automatically() const;
|
||||
virtual bool can_generate_small_preview() const;
|
||||
|
|
@ -123,17 +123,25 @@ class EditorResourcePreview : public Node {
|
|||
void _update_thumbnail_sizes();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static EditorResourcePreview *get_singleton();
|
||||
|
||||
struct PreviewItem {
|
||||
Ref<Texture2D> preview;
|
||||
Ref<Texture2D> small_preview;
|
||||
};
|
||||
|
||||
// p_receiver_func callback has signature (String p_path, Ref<Texture2D> p_preview, Ref<Texture2D> p_preview_small, Variant p_userdata)
|
||||
// p_preview will be null if there was an error
|
||||
void queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
|
||||
void queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
|
||||
const Dictionary get_preview_metadata(const String &p_path) const;
|
||||
|
||||
PreviewItem get_resource_preview_if_available(const String &p_path);
|
||||
|
||||
void add_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
|
||||
void remove_preview_generator(const Ref<EditorResourcePreviewGenerator> &p_generator);
|
||||
void check_for_invalidation(const String &p_path);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue