Compare commits

...

42 Commits

Author SHA1 Message Date
Rémi Verschelde dec5a373d9
Merge pull request #113622 from mihe/jolt/no-nested-mt-flushes
Remove call to `PhysicsServer3D` from Jolt Physics implementation
2025-12-05 14:21:06 +07:00
Rémi Verschelde 7e1ef4c9e2
Merge pull request #113268 from nathanjf/particle-process-material
Docs: Fix ParticleProcessMaterial code snippet in `alpha_curve` description
2025-12-05 14:14:40 +07:00
Mikael Hermansson 7efdb83924 Remove call to `PhysicsServer3D` from Jolt Physics implementation 2025-12-05 13:52:42 +07:00
Rémi Verschelde 0156b2bcd6
Merge pull request #113615 from bruvzg/ed_bit_url
[EditorHelpBit] Open online documentation if script editor is not available.
2025-12-05 12:56:10 +07:00
Rémi Verschelde c1bbaca12a
Merge pull request #113600 from HolonProduction/cache-fixup
GDScript: Ensure correct caching of cyclic references
2025-12-05 12:56:06 +07:00
Rémi Verschelde 204e784bca
Merge pull request #113587 from syntaxerror247/popup-menu-seperation
Increase PopupMenu vertical separation to improve touch usability
2025-12-05 12:56:01 +07:00
Rémi Verschelde 9706b4e53a
Merge pull request #113564 from MattiaZir/fix-project-import-error-parent-child
Fix signal order for directory selection in `file_dialog`
2025-12-05 12:55:57 +07:00
Rémi Verschelde 3135a40699
Merge pull request #113525 from brycehutchings/fix_nonpod_memcpy
Fix crash in `command_queue_mt` due to destruction of wrong object
2025-12-05 12:55:53 +07:00
Rémi Verschelde 8981cede94
Merge pull request #113235 from timothyqiu/motif-close
X11: Skip Motif function hints when borderless
2025-12-05 12:55:49 +07:00
Rémi Verschelde 6b420d701a
Merge pull request #110080 from bruvzg/popscwpc
[GraphEdit] Do not scale popup menus in the graph elements when zoomed.
2025-12-05 12:55:44 +07:00
Nathan Frank a6f81f03b3
Docs: Fix ParticleProcessMaterial code snippet in `alpha_curve` description 2025-12-05 12:41:06 +07:00
Pāvels Nadtočajevs 330a19f362
[Editor HelpBit] Open online documentation if script editor is not available. 2025-12-05 12:10:28 +07:00
HolonProduction ab0397878c GDScript: Ensure correct caching of cyclic references 2025-12-05 10:41:06 +07:00
Rémi Verschelde 3edd256a6f
Merge pull request #113611 from bruvzg/mod_plus
[Editor] Fix missing "+" in the bunch of tooltips.
2025-12-05 09:38:51 +07:00
Rémi Verschelde eb389feaf2
Merge pull request #113601 from Giganzo/inspector-control-height
Fix property height in the inspector for control layout
2025-12-05 09:38:46 +07:00
Rémi Verschelde ed02f9e03d
Merge pull request #113599 from allenwp/fix-glow-bleed-threshold-error-value
Change `environment_get_glow_hdr_bleed_threshold` error handling
2025-12-05 09:38:43 +07:00
Rémi Verschelde 4b448fdd19
Merge pull request #113585 from brycehutchings/fix_d3d12_array_multisample
Fix bad D3D12 SRV breaking MSAA with OpenXR
2025-12-05 09:38:38 +07:00
Rémi Verschelde 09abc05878
Merge pull request #113581 from bruvzg/meh_fd
[FileDialog] Use base dir instead of ".." when going up.
2025-12-05 09:38:34 +07:00
Rémi Verschelde 94eeedd4d6
Merge pull request #113580 from HolonProduction/path-fixup
Correctly set GDScript internal path for shallow scripts
2025-12-05 09:38:29 +07:00
Rémi Verschelde fdd47084bf
Merge pull request #113578 from syntaxerror247/export-dialog-size
Reduce export dialog minimum size
2025-12-05 09:38:25 +07:00
Rémi Verschelde 8bff45aa36
Merge pull request #113573 from MattiaZir/fix-color-picker-crash
Fix memory management for ColorPalette in save file dialog
2025-12-05 09:38:21 +07:00
Rémi Verschelde 27b185955d
Merge pull request #113572 from KoBeWi/bugnail
Fix wrong file thumbnails icon
2025-12-05 09:38:17 +07:00
Rémi Verschelde b3cec96b24
Merge pull request #113366 from aaronp64/free_gamestate_snapshots
Fix `GameStateSnapshot`s not being freed
2025-12-05 09:38:13 +07:00
Rémi Verschelde 6b0a74073c
Merge pull request #113296 from KoBeWi/soft_open_gently
Fix dock opening focus
2025-12-05 09:38:08 +07:00
Rémi Verschelde 724054c1ab
Merge pull request #90439 from kitbdev/fix-dock-resizing-multi
Use multiple children in dock SplitContainers to make resizing consistent
2025-12-05 09:38:00 +07:00
Pāvels Nadtočajevs e2b8c9f038
[Editor] Fix missing "+" in the bunch of tooltips. 2025-12-05 10:20:50 +07:00
kit 8dfcae15fd Use multiple children for dock SplitContainers 2025-12-04 23:05:14 +07:00
Giganzo c01711ca16 Fix property height in the inspector for control layout 2025-12-05 00:06:24 +07:00
Allen Pestaluky 6f02377272 Change `environment_get_glow_hdr_bleed_threshold` error handling to use default value. 2025-12-04 17:42:46 +07:00
Anish Kumar df4f9d0778 Increase PopupMenu vertical separation to improve touch usability 2025-12-05 01:46:45 +07:00
Bryce Hutchings ed4bc24f9b Fix bad D3D12 SRV breaking MSAA with OpenXR 2025-12-04 12:00:13 +07:00
HolonProduction 2ce3be221a Correctly set GDScript internal path for shallow scripts 2025-12-04 20:44:00 +07:00
aaronp64 ed6181c943 Fix GameStateSnapshots not being freed
Updated GameStateSnapshot to inherit from RefCounted, to be automatically deleted when reference count reaches zero, and removed GameStateSnapshotRef wrapper class.
2025-12-04 14:42:40 +07:00
Pāvels Nadtočajevs ccd84d79a7
[FileDialog] Use base dir instead of ".." when going up. 2025-12-04 21:12:20 +07:00
Anish Kumar c4e49b4aed Reduce export dialog minimum size 2025-12-04 22:31:57 +07:00
Bryce Hutchings cb4d1b79e0 Fix crash in command_queue_mt due to destruction of wrong object 2025-12-04 08:46:51 +07:00
mattia.zirpoli bf85f96a8e Fix memory management for ColorPalette in save file dialog 2025-12-04 17:38:02 +07:00
mattia.zirpoli 98ccfb07c1 Fix signal order for directory selection in `file_dialog` 2025-12-04 17:23:10 +07:00
kobewi 31f6413717 Fix wrong file thumbnails icon 2025-12-04 17:02:24 +07:00
kobewi cea95e6004 Fix dock opening focus 2025-11-29 00:44:01 +07:00
Haoyu Qiu f694c64ac4 X11: Skip Motif function hints when borderless 2025-11-27 21:53:28 +07:00
Pāvels Nadtočajevs bdca5fa3a3
[GraphEdit] Do not scale popup menus in the graph elements when zoomed. 2025-11-27 13:40:14 +07:00
42 changed files with 252 additions and 192 deletions

@ -60,7 +60,7 @@ class CommandQueueMT {
_FORCE_INLINE_ Command(T *p_instance, M p_method, FwdArgs &&...p_args) :
CommandBase(NeedsSync), instance(p_instance), method(p_method), args(std::forward<FwdArgs>(p_args)...) {}
void call() {
void call() override {
call_impl(BuildIndexSequence<sizeof...(Args)>{});
}
@ -128,7 +128,7 @@ class CommandQueueMT {
command_mem.resize(size + alloc_size + sizeof(uint64_t));
*(uint64_t *)&command_mem[size] = alloc_size;
void *cmd = &command_mem[size + sizeof(uint64_t)];
new (cmd) T(std::forward<Args>(p_args)...);
memnew_placement(cmd, T(std::forward<Args>(p_args)...));
pending.store(true);
}
@ -164,44 +164,39 @@ class CommandQueueMT {
return;
}
char cmd_backup[MAX_COMMAND_SIZE];
alignas(uint64_t) char cmd_local_mem[MAX_COMMAND_SIZE];
while (flush_read_ptr < command_mem.size()) {
uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr];
flush_read_ptr += sizeof(uint64_t);
CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
// Protect against race condition between this thread
// during the call to the command and other threads potentially
// invalidating the pointer due to reallocs.
memcpy(cmd_backup, (char *)cmd, size);
// invalidating the pointer due to reallocs by relocating the object.
CommandBase *cmd_original = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
CommandBase *cmd_local = reinterpret_cast<CommandBase *>(cmd_local_mem);
memcpy(cmd_local_mem, (char *)cmd_original, size);
if (unique_flusher) {
// A single thread will pump; the lock is only needed for the command queue itself.
lock.temp_unlock();
((CommandBase *)cmd_backup)->call();
cmd_local->call();
lock.temp_relock();
} else {
// At least we can unlock during WTP operations.
uint32_t allowance_id = WorkerThreadPool::thread_enter_unlock_allowance_zone(lock);
((CommandBase *)cmd_backup)->call();
cmd_local->call();
WorkerThreadPool::thread_exit_unlock_allowance_zone(allowance_id);
}
// Handle potential realloc due to the command and unlock allowance.
cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
if (unlikely(cmd->sync)) {
if (unlikely(cmd_local->sync)) {
sync_head++;
lock.temp_unlock(); // Give an opportunity to awaiters right away.
sync_cond_var.notify_all();
lock.temp_relock();
// Handle potential realloc happened during unlock.
cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
}
cmd->~CommandBase();
cmd_local->~CommandBase();
flush_read_ptr += size;
}

@ -77,6 +77,7 @@
<return type="void" />
<description>
Opens the dock. It will appear in the last used dock slot. If the dock has no default slot, it will be opened floating.
[b]Note:[/b] This does not focus the dock. If you want to open and focus the dock, use [method make_visible].
</description>
</method>
</methods>

@ -1239,7 +1239,7 @@
[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
</member>
<member name="interface/touchscreen/enable_touch_optimizations" type="bool" setter="" getter="">
If [code]true[/code], increases the scrollbar touch area and enables a larger dragger for split containers to improve usability on touchscreen devices
If [code]true[/code], increases the scrollbar touch area, enables a larger dragger for split containers, and increases PopupMenu vertical separation to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices.
</member>
<member name="interface/touchscreen/scale_gizmo_handles" type="float" setter="" getter="">

@ -19,6 +19,9 @@
If [code]true[/code], the user can resize the GraphElement.
[b]Note:[/b] Dragging the handle will only emit the [signal resize_request] and [signal resize_end] signals, the GraphElement needs to be resized manually.
</member>
<member name="scaling_menus" type="bool" setter="set_scaling_menus" getter="is_scaling_menus" default="false">
If [code]true[/code], [PopupMenu]s that are descendants of the GraphElement are scaled with the [GraphEdit] zoom.
</member>
<member name="selectable" type="bool" setter="set_selectable" getter="is_selectable" default="true">
If [code]true[/code], the user can select the GraphElement.
</member>

@ -90,7 +90,7 @@
<members>
<member name="alpha_curve" type="Texture2D" setter="set_alpha_curve" getter="get_alpha_curve">
The alpha value of each particle's color will be multiplied by this [CurveTexture] over its lifetime.
[b]Note:[/b] [member alpha_curve] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member alpha_curve] will have no visible effect.
[b]Note:[/b] [member alpha_curve] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALPHA *= COLOR.a;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member alpha_curve] will have no visible effect.
</member>
<member name="angle_curve" type="Texture2D" setter="set_param_texture" getter="get_param_texture">
Each particle's rotation will be animated along this [CurveTexture].

@ -1687,7 +1687,7 @@ RDD::TextureID RenderingDeviceDriverD3D12::_texture_create_shared_from_slice(Tex
uav_desc.Texture2DArray.ArraySize = 1;
uav_desc.Texture2DArray.PlaneSlice = 0;
} else if ((srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY || (srv_desc.ViewDimension == D3D12_SRV_DIMENSION_TEXTURE2DMS && p_layer))) {
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
srv_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
srv_desc.Texture2DMSArray.FirstArraySlice = p_layer;
srv_desc.Texture2DMSArray.ArraySize = 1;

@ -2130,15 +2130,9 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue"));
docontinue->connect(SceneStringName(pressed), callable_mp(this, &ScriptEditorDebugger::debug_continue));
HSplitContainer *parent_sc = memnew(HSplitContainer);
vbc->add_child(parent_sc);
parent_sc->set_v_size_flags(SIZE_EXPAND_FILL);
parent_sc->set_split_offset(500 * EDSCALE);
HSplitContainer *sc = memnew(HSplitContainer);
sc->set_v_size_flags(SIZE_EXPAND_FILL);
sc->set_h_size_flags(SIZE_EXPAND_FILL);
parent_sc->add_child(sc);
vbc->add_child(sc);
VBoxContainer *stack_vb = memnew(VBoxContainer);
stack_vb->set_h_size_flags(SIZE_EXPAND_FILL);
@ -2204,7 +2198,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() {
breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked));
breakpoints_tree->create_item();
parent_sc->add_child(breakpoints_tree);
sc->add_child(breakpoints_tree);
tabs->add_child(dbg);
breakpoints_menu = memnew(PopupMenu);

@ -4218,9 +4218,63 @@ void EditorHelpBit::_update_labels() {
}
}
void EditorHelpBit::_go_to_url(const String &p_what) {
Vector<String> parts;
{
int from = 0;
int buffer_start = 0;
while (true) {
const int pos = p_what.find_char(':', from);
if (pos < 0) {
parts.push_back(p_what.substr(buffer_start));
break;
}
if (pos + 1 < p_what.length() && p_what[pos + 1] == ':') {
// `::` used in built-in scripts.
from = pos + 2;
} else {
parts.push_back(p_what.substr(buffer_start, pos - buffer_start));
from = pos + 1;
buffer_start = from;
}
}
}
const String what = parts[0]; // `parts` is always non-empty.
const String clss = (parts.size() > 1) ? parts[1].to_lower() : String();
const String name = (parts.size() > 2) ? parts[2].to_lower().replace_chars("/_", '-') : String();
String section = "";
if (what == "class_desc") {
section = "#description";
} else if (what == "class_signal") {
section = vformat("#class-%s-signal-%s", clss, name);
} else if (what == "class_method" || what == "class_method_desc") {
section = vformat("#class-%s-method-%s", clss, name);
} else if (what == "class_property") {
section = vformat("#class-%s-property-%s", clss, name);
} else if (what == "class_enum") {
section = vformat("#enum-%s-%s", clss, name);
} else if (what == "class_theme_item") {
section = vformat("#class-%s-theme-%s", clss, name);
} else if (what == "class_constant") {
section = vformat("#class-%s-constant-%s", clss, name);
} else if (what == "class_annotation") {
section = vformat("#%s", clss);
}
String doc_url = clss.is_empty() ? String(GODOT_VERSION_DOCS_URL "/") : vformat(GODOT_VERSION_DOCS_URL "/classes/class_%s.html%s", clss, section);
OS::get_singleton()->shell_open(doc_url);
}
void EditorHelpBit::_go_to_help(const String &p_what) {
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
ScriptEditor::get_singleton()->goto_help(p_what);
if (ScriptEditor::get_singleton()) {
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT);
ScriptEditor::get_singleton()->goto_help(p_what);
} else {
_go_to_url(p_what);
}
emit_signal(SNAME("request_hide"));
}

@ -344,6 +344,7 @@ class EditorHelpBit : public VBoxContainer {
void _add_type_to_title(const DocType &p_doc_type);
void _update_labels();
void _go_to_help(const String &p_what);
void _go_to_url(const String &p_what);
void _meta_clicked(const String &p_select);
protected:

@ -111,7 +111,7 @@ EditorDock::EditorDock() {
void EditorDock::open() {
if (!is_open) {
EditorDockManager::get_singleton()->open_dock(this);
EditorDockManager::get_singleton()->open_dock(this, false);
}
}

@ -673,8 +673,15 @@ 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), int(hsplits[i]->get_split_offset() / EDSCALE));
PackedInt32Array split_offsets = main_hsplit->get_split_offsets();
int index = 0;
for (int i = 0; i < vsplits.size(); i++) {
int value = 0;
if (vsplits[i]->is_visible() && index < split_offsets.size()) {
value = split_offsets[index] / EDSCALE;
index++;
}
p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), value);
}
}
@ -755,21 +762,22 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
}
// Load SplitContainer offsets.
PackedInt32Array offsets;
for (int i = 0; i < vsplits.size(); i++) {
if (!p_layout->has_section_key(p_section, "dock_split_" + itos(i + 1))) {
continue;
}
int ofs = p_layout->get_value(p_section, "dock_split_" + itos(i + 1));
vsplits[i]->set_split_offset(ofs);
}
for (int i = 0; i < hsplits.size(); i++) {
if (!p_layout->has_section_key(p_section, "dock_hsplit_" + itos(i + 1))) {
continue;
// Only visible ones need a split offset for the main hsplit, even though they all have a value saved.
if (vsplits[i]->is_visible() && p_layout->has_section_key(p_section, "dock_hsplit_" + itos(i + 1))) {
int offset = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
offsets.push_back(offset * EDSCALE);
}
int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
hsplits[i]->set_split_offset(ofs * EDSCALE);
}
main_hsplit->set_split_offsets(offsets);
update_docks_menu();
}
@ -942,8 +950,8 @@ void EditorDockManager::add_vsplit(DockSplitContainer *p_split) {
p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged));
}
void EditorDockManager::add_hsplit(DockSplitContainer *p_split) {
hsplits.push_back(p_split);
void EditorDockManager::set_hsplit(DockSplitContainer *p_split) {
main_hsplit = p_split;
p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged));
}
@ -983,10 +991,6 @@ void EditorDockManager::register_dock_slot(DockConstants::DockSlot p_dock_slot,
slot.drag_hint->set_slot(p_dock_slot);
}
int EditorDockManager::get_hsplit_count() const {
return hsplits.size();
}
int EditorDockManager::get_vsplit_count() const {
return vsplits.size();
}

@ -86,7 +86,7 @@ private:
// To access splits easily by index.
Vector<DockSplitContainer *> vsplits;
Vector<DockSplitContainer *> hsplits;
DockSplitContainer *main_hsplit = nullptr;
struct DockSlot {
TabContainer *container = nullptr;
@ -133,9 +133,8 @@ public:
void set_tab_icon_max_width(int p_max_width);
void add_vsplit(DockSplitContainer *p_split);
void add_hsplit(DockSplitContainer *p_split);
void set_hsplit(DockSplitContainer *p_split);
void register_dock_slot(DockConstants::DockSlot p_dock_slot, TabContainer *p_tab_container, DockConstants::DockLayout p_layout);
int get_hsplit_count() const;
int get_vsplit_count() const;
PopupMenu *get_docks_menu();

@ -7764,7 +7764,6 @@ void EditorNode::_update_main_menu_type() {
#ifdef ANDROID_ENABLED
// Align main menu icon visually with TouchActionsPanel buttons.
main_menu_button->get_popup()->add_theme_constant_override("v_separation", 16 * EDSCALE);
menu_btn_spacer = memnew(Control);
menu_btn_spacer->set_custom_minimum_size(Vector2(8, 0) * EDSCALE);
title_bar->add_child(menu_btn_spacer);
@ -8235,16 +8234,15 @@ EditorNode::EditorNode() {
main_vbox->add_child(title_bar);
#endif
left_l_hsplit = memnew(DockSplitContainer);
left_l_hsplit->set_name("DockHSplitLeftL");
main_vbox->add_child(left_l_hsplit);
left_l_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
main_hsplit = memnew(DockSplitContainer);
main_hsplit->set_name("DockHSplitMain");
main_hsplit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
main_vbox->add_child(main_hsplit);
left_l_vsplit = memnew(DockSplitContainer);
left_l_vsplit->set_name("DockVSplitLeftL");
left_l_vsplit->set_vertical(true);
left_l_hsplit->add_child(left_l_vsplit);
main_hsplit->add_child(left_l_vsplit);
TabContainer *dock_slot[DockConstants::DOCK_SLOT_MAX];
dock_slot[DockConstants::DOCK_SLOT_LEFT_UL] = memnew(TabContainer);
@ -8254,13 +8252,10 @@ EditorNode::EditorNode() {
dock_slot[DockConstants::DOCK_SLOT_LEFT_BL]->set_name("DockSlotLeftBL");
left_l_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_BL]);
left_r_hsplit = memnew(DockSplitContainer);
left_r_hsplit->set_name("DockHSplitLeftR");
left_l_hsplit->add_child(left_r_hsplit);
left_r_vsplit = memnew(DockSplitContainer);
left_r_vsplit->set_name("DockVSplitLeftR");
left_r_vsplit->set_vertical(true);
left_r_hsplit->add_child(left_r_vsplit);
main_hsplit->add_child(left_r_vsplit);
dock_slot[DockConstants::DOCK_SLOT_LEFT_UR] = memnew(TabContainer);
dock_slot[DockConstants::DOCK_SLOT_LEFT_UR]->set_name("DockSlotLeftUR");
left_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_UR]);
@ -8268,13 +8263,9 @@ EditorNode::EditorNode() {
dock_slot[DockConstants::DOCK_SLOT_LEFT_BR]->set_name("DockSlotLeftBR");
left_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_BR]);
main_hsplit = memnew(DockSplitContainer);
main_hsplit->set_name("DockHSplitMain");
left_r_hsplit->add_child(main_hsplit);
VBoxContainer *center_vb = memnew(VBoxContainer);
main_hsplit->add_child(center_vb);
center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
main_hsplit->add_child(center_vb);
center_split = memnew(DockSplitContainer);
center_split->set_name("DockVSplitCenter");
@ -8284,14 +8275,10 @@ EditorNode::EditorNode() {
center_vb->add_child(center_split);
center_split->connect("drag_ended", callable_mp(this, &EditorNode::_bottom_panel_resized));
right_hsplit = memnew(DockSplitContainer);
right_hsplit->set_name("DockHSplitRight");
main_hsplit->add_child(right_hsplit);
right_l_vsplit = memnew(DockSplitContainer);
right_l_vsplit->set_name("DockVSplitRightL");
right_l_vsplit->set_vertical(true);
right_hsplit->add_child(right_l_vsplit);
main_hsplit->add_child(right_l_vsplit);
dock_slot[DockConstants::DOCK_SLOT_RIGHT_UL] = memnew(TabContainer);
dock_slot[DockConstants::DOCK_SLOT_RIGHT_UL]->set_name("DockSlotRightUL");
right_l_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_RIGHT_UL]);
@ -8302,7 +8289,7 @@ EditorNode::EditorNode() {
right_r_vsplit = memnew(DockSplitContainer);
right_r_vsplit->set_name("DockVSplitRightR");
right_r_vsplit->set_vertical(true);
right_hsplit->add_child(right_r_vsplit);
main_hsplit->add_child(right_r_vsplit);
dock_slot[DockConstants::DOCK_SLOT_RIGHT_UR] = memnew(TabContainer);
dock_slot[DockConstants::DOCK_SLOT_RIGHT_UR]->set_name("DockSlotRightUR");
right_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_RIGHT_UR]);
@ -8318,10 +8305,7 @@ EditorNode::EditorNode() {
editor_dock_manager->add_vsplit(right_l_vsplit);
editor_dock_manager->add_vsplit(right_r_vsplit);
editor_dock_manager->add_hsplit(left_l_hsplit);
editor_dock_manager->add_hsplit(left_r_hsplit);
editor_dock_manager->add_hsplit(main_hsplit);
editor_dock_manager->add_hsplit(right_hsplit);
editor_dock_manager->set_hsplit(main_hsplit);
for (int i = 0; i < DockConstants::DOCK_SLOT_BOTTOM; i++) {
editor_dock_manager->register_dock_slot((DockConstants::DockSlot)i, dock_slot[i], DockConstants::DOCK_LAYOUT_VERTICAL);
@ -8781,9 +8765,11 @@ EditorNode::EditorNode() {
history_dock = memnew(HistoryDock);
editor_dock_manager->add_dock(history_dock);
// Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize.
left_r_hsplit->set_split_offset(280 * EDSCALE);
main_hsplit->set_split_offset(-280 * EDSCALE);
// Add some offsets to make LEFT_R and RIGHT_L docks wider than minsize.
const int dock_hsize = 280;
// By default there is only 3 visible, so set 2 split offsets for them.
const int dock_hsize_scaled = dock_hsize * EDSCALE;
main_hsplit->set_split_offsets({ dock_hsize_scaled, -dock_hsize_scaled });
// Define corresponding default layout.
@ -8794,9 +8780,8 @@ EditorNode::EditorNode() {
default_layout->set_value(docks_section, "dock_4", "FileSystem,History");
default_layout->set_value(docks_section, "dock_5", "Inspector,Signals,Groups");
int hsplits[] = { 0, 280, -280, 0 };
DEV_ASSERT((int)std_size(hsplits) == editor_dock_manager->get_hsplit_count());
for (int i = 0; i < editor_dock_manager->get_hsplit_count(); i++) {
int hsplits[] = { 0, dock_hsize, -dock_hsize, 0 };
for (int i = 0; i < (int)std_size(hsplits); i++) {
default_layout->set_value(docks_section, "dock_hsplit_" + itos(i + 1), hsplits[i]);
}
for (int i = 0; i < editor_dock_manager->get_vsplit_count(); i++) {

@ -298,12 +298,9 @@ private:
ConfirmationDialog *video_restart_dialog = nullptr;
// Split containers.
DockSplitContainer *left_l_hsplit = nullptr;
DockSplitContainer *left_l_vsplit = nullptr;
DockSplitContainer *left_r_hsplit = nullptr;
DockSplitContainer *left_r_vsplit = nullptr;
DockSplitContainer *main_hsplit = nullptr;
DockSplitContainer *right_hsplit = nullptr;
DockSplitContainer *right_l_vsplit = nullptr;
DockSplitContainer *right_r_vsplit = nullptr;
DockSplitContainer *center_split = nullptr;

@ -1592,10 +1592,18 @@ ProjectExportDialog::ProjectExportDialog() {
top_settings->set_h_size_flags(Control::SIZE_EXPAND_FILL);
panel->add_child(top_settings);
HBoxContainer *name_hbox = memnew(HBoxContainer);
Label *name_label = memnew(Label);
name_label->set_theme_type_variation("HeaderSmall");
name_label->set_text(TTR("Name:"));
name_hbox->add_child(name_label);
name = memnew(LineEdit);
top_settings->add_margin_child(TTR("Name:"), name);
name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
name->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectExportDialog::_name_changed));
name->connect(SceneStringName(focus_exited), callable_mp(this, &ProjectExportDialog::_name_editing_finished));
name_hbox->add_child(name);
top_settings->add_child(name_hbox);
runnable = memnew(CheckButton);
runnable->set_text(TTR("Runnable"));
@ -1724,9 +1732,15 @@ ProjectExportDialog::ProjectExportDialog() {
// Patching.
ScrollContainer *patch_scroll_container = memnew(ScrollContainer);
patch_scroll_container->set_name(TTRC("Patching"));
patch_scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
sections->add_child(patch_scroll_container);
VBoxContainer *patch_vb = memnew(VBoxContainer);
sections->add_child(patch_vb);
patch_vb->set_name(TTRC("Patching"));
patch_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
patch_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
patch_scroll_container->add_child(patch_vb);
patch_delta_encoding = memnew(CheckButton);
patch_delta_encoding->connect(SceneStringName(toggled), callable_mp(this, &ProjectExportDialog::_patch_delta_encoding_changed));

@ -1704,7 +1704,7 @@ void CodeTextEditor::_notification(int p_what) {
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()));
vformat(TTR("%s+Mouse 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()));
[[fallthrough]];
}

@ -855,7 +855,7 @@ void GameView::_camera_override_menu_id_pressed(int p_id) {
void GameView::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Alt+RMB: Show list of all nodes at position clicked."));
select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_tooltip_text(vformat(TTR("%s+Alt+RMB: Show list of all nodes at position clicked."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)));
_update_ui();
} break;

@ -850,7 +850,7 @@ Path2DEditor::Path2DEditor() {
curve_edit->set_toggle_mode(true);
curve_edit->set_pressed(true);
curve_edit->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + vformat(TTR("%s+Click: Add Point"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
curve_edit->set_accessibility_name(TTRC("Select Points"));
curve_edit->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_EDIT));
toolbar->add_child(curve_edit);

@ -2189,7 +2189,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
paint_tool_button->set_shortcut(ED_GET_SHORTCUT("tiles_editor/paint_tool"));
paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Shift: Draw rectangle."));
paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + vformat(TTR("%s+Shift: Draw rectangle."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)));
paint_tool_button->connect(SceneStringName(pressed), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_toolbar));
paint_tool_button->set_accessibility_name(TTRC("Paint Tool"));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);

@ -373,7 +373,7 @@ void TileMapEditorPlugin::_update_tile_map() {
Ref<TileSet> tile_set = edited_layer->get_tile_set();
if (tile_set.is_valid() && tile_set_id != tile_set->get_instance_id()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_plugin_singleton->make_visible_no_focus();
tile_set_id = tile_set->get_instance_id();
} else if (tile_set.is_null()) {
tile_set_plugin_singleton->edit(nullptr);
@ -410,7 +410,7 @@ void TileMapEditorPlugin::_edit_tile_map_layer(TileMapLayer *p_tile_map_layer, b
Ref<TileSet> tile_set = p_tile_map_layer->get_tile_set();
if (tile_set.is_valid()) {
tile_set_plugin_singleton->edit(tile_set.ptr());
tile_set_plugin_singleton->make_visible(true);
tile_set_plugin_singleton->make_visible_no_focus();
tile_set_id = tile_set->get_instance_id();
} else {
tile_set_plugin_singleton->edit(nullptr);
@ -479,7 +479,7 @@ bool TileMapEditorPlugin::handles(Object *p_object) const {
void TileMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
editor->open();
editor->make_visible();
} else {
editor->close();
TileSetEditor::get_singleton()->close();
@ -533,12 +533,16 @@ bool TileSetEditorPlugin::handles(Object *p_object) const {
void TileSetEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
editor->open();
editor->make_visible();
} else {
editor->close();
}
}
void TileSetEditorPlugin::make_visible_no_focus() {
editor->open();
}
ObjectID TileSetEditorPlugin::get_edited_tileset() const {
return edited_tileset;
}

@ -165,6 +165,7 @@ public:
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
void make_visible_no_focus();
ObjectID get_edited_tileset() const;
TileSetEditorPlugin();

@ -8899,10 +8899,10 @@ void Node3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
tool_button[TOOL_MODE_SELECT]->set_tooltip_text(TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)"));
tool_button[TOOL_MODE_TRANSFORM]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)"));
tool_button[TOOL_MODE_MOVE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Use snap.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_ROTATE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Use snap.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_SCALE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Use snap.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_TRANSFORM]->set_tooltip_text(vformat(TTR("%s+Drag: Rotate selected node around pivot."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)"));
tool_button[TOOL_MODE_MOVE]->set_tooltip_text(vformat(TTR("%s+Drag: Use snap."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_ROTATE]->set_tooltip_text(vformat(TTR("%s+Drag: Use snap."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
tool_button[TOOL_MODE_SCALE]->set_tooltip_text(vformat(TTR("%s+Drag: Use snap."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
_update_gizmos_menu();
} break;

@ -913,7 +913,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() {
curve_edit->set_theme_type_variation(SceneStringName(FlatButton));
curve_edit->set_toggle_mode(true);
curve_edit->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Click: Select multiple Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Click: Select multiple Points") + "\n" + vformat(TTR("%s+Click: Add Point"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Right Click: Delete Point"));
curve_edit->set_accessibility_name(TTRC("Select Points"));
toolbar->add_child(curve_edit);
curve_edit->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_EDIT));

@ -4360,7 +4360,7 @@ void CanvasItemEditor::_project_settings_changed() {
void CanvasItemEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
select_button->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)") + "\n" + TTR("RMB: Add node at position clicked."));
select_button->set_tooltip_text(vformat(TTR("%s+Drag: Rotate selected node around pivot."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + vformat(TTR("%s+Alt+Drag: Scale selected node."), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + TTR("(Available in all modes.)") + "\n" + TTR("RMB: Add node at position clicked."));
pivot_button->set_tooltip_text(TTR("Click to change object's pivot.") + "\n" + TTR("Shift: Set temporary pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary pivot in the center of the selected nodes."));
} break;

@ -244,6 +244,7 @@ EditorPropertyAnchorsPreset::EditorPropertyAnchorsPreset() {
options = memnew(OptionButton);
options->set_clip_text(true);
options->set_flat(true);
options->set_theme_type_variation(SNAME("EditorInspectorButton"));
add_child(options);
add_focusable(options);
options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyAnchorsPreset::_option_selected));
@ -426,6 +427,7 @@ EditorPropertySizeFlags::EditorPropertySizeFlags() {
flag_presets = memnew(OptionButton);
flag_presets->set_clip_text(true);
flag_presets->set_flat(true);
flag_presets->set_theme_type_variation(SNAME("EditorInspectorButton"));
vb->add_child(flag_presets);
add_focusable(flag_presets);
set_label_reference(flag_presets);

@ -1107,7 +1107,8 @@ void ThemeClassic::populate_standard_styles(const Ref<EditorTheme> &p_theme, Edi
p_theme->set_icon("submenu", "PopupMenu", p_theme->get_icon(SNAME("ArrowRight"), EditorStringName(EditorIcons)));
p_theme->set_icon("submenu_mirrored", "PopupMenu", p_theme->get_icon(SNAME("ArrowLeft"), EditorStringName(EditorIcons)));
p_theme->set_constant("v_separation", "PopupMenu", p_config.forced_even_separation * EDSCALE);
int v_sep = (p_config.enable_touch_optimizations ? 12 : p_config.forced_even_separation) * EDSCALE;
p_theme->set_constant("v_separation", "PopupMenu", v_sep);
p_theme->set_constant("outline_size", "PopupMenu", 0);
p_theme->set_constant("item_start_padding", "PopupMenu", p_config.separation_margin);
p_theme->set_constant("item_end_padding", "PopupMenu", p_config.separation_margin);

@ -1046,7 +1046,8 @@ void ThemeModern::populate_standard_styles(const Ref<EditorTheme> &p_theme, Edit
p_theme->set_icon("submenu_mirrored", "PopupMenu", p_theme->get_icon(SNAME("ArrowLeft"), EditorStringName(EditorIcons)));
p_theme->set_constant("h_separation", "PopupMenu", p_config.base_margin * 1.75 * EDSCALE);
p_theme->set_constant("v_separation", "PopupMenu", p_config.base_margin * 1.75 * EDSCALE);
int v_sep = (p_config.enable_touch_optimizations ? 12 : p_config.base_margin * 1.75) * EDSCALE;
p_theme->set_constant("v_separation", "PopupMenu", v_sep);
p_theme->set_constant("outline_size", "PopupMenu", 0);
p_theme->set_constant("item_start_padding", "PopupMenu", p_config.popup_margin);
p_theme->set_constant("item_end_padding", "PopupMenu", p_config.popup_margin);

@ -1078,6 +1078,13 @@ void GDScript::set_path_cache(const String &p_path) {
if (is_root_script()) {
Script::set_path_cache(p_path);
}
path = p_path;
path_valid = true;
for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) {
kv.value->set_path_cache(p_path);
}
}
void GDScript::set_path(const String &p_path, bool p_take_over) {
@ -3047,11 +3054,7 @@ Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String
Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
Error err;
bool ignoring = p_cache_mode == CACHE_MODE_IGNORE || p_cache_mode == CACHE_MODE_IGNORE_DEEP;
Ref<GDScript> scr = GDScriptCache::get_full_script_no_resource_cache(p_original_path, err, "", ignoring);
// Reset `path_cache` so that when resource loader uses `set_path()` later, the script gets added to the cache.
if (scr.is_valid()) {
scr->set_path_cache(String());
}
Ref<GDScript> scr = GDScriptCache::get_full_script(p_original_path, err, "", ignoring);
if (err && scr.is_valid()) {
// If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.

@ -153,7 +153,7 @@ thread_local SafeBinaryMutex<GDScriptCache::BINARY_MUTEX_TAG>::TLSData SafeBinar
SafeBinaryMutex<GDScriptCache::BINARY_MUTEX_TAG> GDScriptCache::mutex;
void GDScriptCache::move_script(const String &p_from, const String &p_to) {
if (singleton == nullptr || p_from == p_to) {
if (singleton == nullptr || p_from == p_to || p_from.is_empty()) {
return;
}
@ -339,17 +339,6 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, Error &r_e
}
Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner, bool p_update_from_disk) {
Ref<GDScript> uncached_script = get_full_script_no_resource_cache(p_path, r_error, p_owner, p_update_from_disk);
// Resources don't know whether they are cached, so using `set_path()` after `set_path_cache()` does not add the resource to the cache if the path is the same.
// We reset the cached path from `get_shallow_script()` so that the subsequent call to `set_path()` caches everything correctly.
uncached_script->set_path_cache(String());
uncached_script->set_path(p_path, true);
return uncached_script;
}
Ref<GDScript> GDScriptCache::get_full_script_no_resource_cache(const String &p_path, Error &r_error, const String &p_owner, bool p_update_from_disk) {
MutexLock lock(singleton->mutex);
if (!p_owner.is_empty() && p_path != p_owner) {
@ -403,6 +392,12 @@ Ref<GDScript> GDScriptCache::get_full_script_no_resource_cache(const String &p_p
singleton->full_gdscript_cache[p_path] = script;
singleton->shallow_gdscript_cache.erase(p_path);
// Add the script to the resource cache. Usually ResourceLoader would take care of it, but cyclic references can break that sometimes so we do it ourselves.
// Resources don't know whether they are cached, so using `set_path()` after `set_path_cache()` does not add the resource to the cache if the path is the same.
// We reset the cached path from `get_shallow_script()` so that the subsequent call to `set_path()` caches everything correctly.
script->set_path_cache(String());
script->set_path(p_path, true);
return script;
}

@ -121,14 +121,6 @@ public:
static String get_source_code(const String &p_path);
static Vector<uint8_t> get_binary_tokens(const String &p_path);
static Ref<GDScript> get_shallow_script(const String &p_path, Error &r_error, const String &p_owner = String());
/**
* Returns a fully loaded GDScript using an already cached script if one exists.
*
* If a new script is created, the resource will only have its patch_cache set, so it won't be present in the ResourceCache.
* Mismatches between GDScriptCache and ResourceCache might trigger complex issues so when using this method ensure
* that the script is added to the resource cache or removed from the GDScript cache.
*/
static Ref<GDScript> get_full_script_no_resource_cache(const String &p_path, Error &r_error, const String &p_owner = String(), bool p_update_from_disk = false);
/**
* Returns a fully loaded GDScript using an already cached script if one exists.
*

@ -30,6 +30,7 @@
#include "jolt_joint_3d.h"
#include "../jolt_physics_server_3d.h"
#include "../jolt_project_settings.h"
#include "../misc/jolt_type_conversions.h"
#include "../objects/jolt_body_3d.h"
@ -209,7 +210,7 @@ void JoltJoint3D::set_collision_disabled(bool p_disabled) {
return;
}
PhysicsServer3D *physics_server = PhysicsServer3D::get_singleton();
JoltPhysicsServer3D *physics_server = JoltPhysicsServer3D::get_singleton();
if (collision_disabled) {
physics_server->body_add_collision_exception(body_a->get_rid(), body_b->get_rid());

@ -41,7 +41,9 @@
#include "core/os/time.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/script_editor_debugger.h"
#include "editor/docks/inspector_dock.h"
#include "editor/editor_node.h"
#include "editor/inspector/editor_inspector.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
@ -167,7 +169,7 @@ TreeItem *ObjectDBProfilerPanel::_add_snapshot_button(const String &p_snapshot_f
void ObjectDBProfilerPanel::_show_selected_snapshot() {
if (snapshot_list->get_selected()->get_text(0) == (String)diff_button->get_selected_metadata()) {
for (int i = 0; i < diff_button->get_item_count(); i++) {
if (diff_button->get_item_text(i) == current_snapshot->get_snapshot()->name) {
if (diff_button->get_item_text(i) == current_snapshot->name) {
diff_button->select(i);
break;
}
@ -184,7 +186,7 @@ void ObjectDBProfilerPanel::_on_snapshot_deselected() {
_update_enabled_diff_items();
}
Ref<GameStateSnapshotRef> ObjectDBProfilerPanel::get_snapshot(const String &p_snapshot_file_name) {
Ref<GameStateSnapshot> ObjectDBProfilerPanel::get_snapshot(const String &p_snapshot_file_name) {
if (snapshot_cache.has(p_snapshot_file_name)) {
return snapshot_cache.get(p_snapshot_file_name);
}
@ -201,7 +203,7 @@ Ref<GameStateSnapshotRef> ObjectDBProfilerPanel::get_snapshot(const String &p_sn
Vector<uint8_t> content = snapshot_file->get_buffer(snapshot_file->get_length()); // We want to split on newlines, so normalize them.
ERR_FAIL_COND_V_MSG(content.is_empty(), nullptr, "ObjectDB Snapshot file is empty: " + full_file_path);
Ref<GameStateSnapshotRef> snapshot = GameStateSnapshot::create_ref(p_snapshot_file_name, content);
Ref<GameStateSnapshot> snapshot = GameStateSnapshot::create_ref(p_snapshot_file_name, content);
if (snapshot.is_valid()) {
snapshot_cache.insert(p_snapshot_file_name, snapshot);
}
@ -225,8 +227,8 @@ void ObjectDBProfilerPanel::_view_tab_changed(int p_tab_idx) {
// Populating tabs only on tab changed because we're handling a lot of data,
// and the editor freezes for a while if we try to populate every tab at once.
SnapshotView *view = cast_to<SnapshotView>(view_tabs->get_current_tab_control());
GameStateSnapshot *snapshot = current_snapshot.is_null() ? nullptr : current_snapshot->get_snapshot();
GameStateSnapshot *diff = diff_snapshot.is_null() ? nullptr : diff_snapshot->get_snapshot();
GameStateSnapshot *snapshot = current_snapshot.ptr();
GameStateSnapshot *diff = diff_snapshot.ptr();
if (snapshot != nullptr && !view->is_showing_snapshot(snapshot, diff)) {
view->show_snapshot(snapshot, diff);
}
@ -237,6 +239,11 @@ void ObjectDBProfilerPanel::clear_snapshot(bool p_update_view_tabs) {
view->clear_snapshot();
}
const Object *edited_object = InspectorDock::get_inspector_singleton()->get_edited_object();
if (Object::cast_to<SnapshotDataObject>(edited_object)) {
EditorNode::get_singleton()->push_item(nullptr);
}
current_snapshot.unref();
diff_snapshot.unref();
@ -333,7 +340,7 @@ void ObjectDBProfilerPanel::_edit_snapshot_name() {
ObjectDBProfilerPanel::ObjectDBProfilerPanel() {
set_name(TTRC("ObjectDB Profiler"));
snapshot_cache = LRUCache<String, Ref<GameStateSnapshotRef>>(SNAPSHOT_CACHE_MAX_SIZE);
snapshot_cache = LRUCache<String, Ref<GameStateSnapshot>>(SNAPSHOT_CACHE_MAX_SIZE);
EditorDebuggerNode::get_singleton()->get_current_debugger()->connect("breaked", callable_mp(this, &ObjectDBProfilerPanel::_on_debug_breaked));

@ -69,9 +69,9 @@ protected:
HashMap<int, PartialSnapshot> partial_snapshots;
LocalVector<SnapshotView *> views;
Ref<GameStateSnapshotRef> current_snapshot;
Ref<GameStateSnapshotRef> diff_snapshot;
LRUCache<String, Ref<GameStateSnapshotRef>> snapshot_cache;
Ref<GameStateSnapshot> current_snapshot;
Ref<GameStateSnapshot> diff_snapshot;
LRUCache<String, Ref<GameStateSnapshot>> snapshot_cache;
void _request_object_snapshot();
void _begin_object_snapshot();
@ -95,7 +95,7 @@ public:
void receive_snapshot(int p_request_id);
void show_snapshot(const String &p_snapshot_file_name, const String &p_snapshot_diff_file_name);
void clear_snapshot(bool p_update_view_tabs = true);
Ref<GameStateSnapshotRef> get_snapshot(const String &p_snapshot_file_name);
Ref<GameStateSnapshot> get_snapshot(const String &p_snapshot_file_name);
void set_enabled(bool p_enabled);
void add_view(SnapshotView *p_to_add);

@ -318,11 +318,9 @@ void GameStateSnapshot::recompute_references() {
}
}
Ref<GameStateSnapshotRef> GameStateSnapshot::create_ref(const String &p_snapshot_name, const Vector<uint8_t> &p_snapshot_buffer) {
// A ref to a refcounted object which is a wrapper of a non-refcounted object.
Ref<GameStateSnapshotRef> sn;
sn.instantiate(memnew(GameStateSnapshot));
GameStateSnapshot *snapshot = sn->get_snapshot();
Ref<GameStateSnapshot> GameStateSnapshot::create_ref(const String &p_snapshot_name, const Vector<uint8_t> &p_snapshot_buffer) {
Ref<GameStateSnapshot> snapshot;
snapshot.instantiate();
snapshot->name = p_snapshot_name;
// Snapshots may have been created by an older version of the editor. Handle parsing old snapshot versions here based on the version number.
@ -347,13 +345,13 @@ Ref<GameStateSnapshotRef> GameStateSnapshot::create_ref(const String &p_snapshot
continue;
}
snapshot->objects[obj.id] = memnew(SnapshotDataObject(obj, snapshot, resource_cache));
snapshot->objects[obj.id] = memnew(SnapshotDataObject(obj, snapshot.ptr(), resource_cache));
snapshot->objects[obj.id]->extra_debug_data = (Dictionary)snapshot_data[i + 3];
}
snapshot->recompute_references();
print_verbose("Resource cache hits: " + String::num(resource_cache.hits) + ". Resource cache misses: " + String::num(resource_cache.misses));
return sn;
return snapshot;
}
GameStateSnapshot::~GameStateSnapshot() {
@ -361,15 +359,3 @@ GameStateSnapshot::~GameStateSnapshot() {
memdelete(item.value);
}
}
bool GameStateSnapshotRef::unreference() {
bool die = RefCounted::unreference();
if (die) {
memdelete(gamestate_snapshot);
}
return die;
}
GameStateSnapshot *GameStateSnapshotRef::get_snapshot() {
return gamestate_snapshot;
}

@ -33,7 +33,6 @@
#include "editor/debugger/editor_debugger_inspector.h"
class GameStateSnapshot;
class GameStateSnapshotRef;
class SnapshotDataObject : public Object {
GDCLASS(SnapshotDataObject, Object);
@ -77,8 +76,8 @@ protected:
static void _bind_methods();
};
class GameStateSnapshot : public Object {
GDCLASS(GameStateSnapshot, Object);
class GameStateSnapshot : public RefCounted {
GDCLASS(GameStateSnapshot, RefCounted);
void _get_outbound_references(Variant &p_var, HashMap<String, ObjectID> &r_ret_val, const String &p_current_path = "");
void _get_rc_cycles(SnapshotDataObject *p_obj, SnapshotDataObject *p_source_obj, HashSet<SnapshotDataObject *> p_traversed_objs, LocalVector<String> &r_ret_val, const String &p_current_path = "");
@ -88,24 +87,8 @@ public:
HashMap<ObjectID, SnapshotDataObject *> objects;
Dictionary snapshot_context;
// Ideally, this would extend EditorDebuggerRemoteObject and be refcounted, but we can't have it both ways.
// So, instead we have this static 'constructor' that returns a RefCounted wrapper around a GameStateSnapshot.
static Ref<GameStateSnapshotRef> create_ref(const String &p_snapshot_name, const Vector<uint8_t> &p_snapshot_buffer);
static Ref<GameStateSnapshot> create_ref(const String &p_snapshot_name, const Vector<uint8_t> &p_snapshot_buffer);
~GameStateSnapshot();
void recompute_references();
};
// Thin RefCounted wrapper around a GameStateSnapshot.
class GameStateSnapshotRef : public RefCounted {
GDCLASS(GameStateSnapshotRef, RefCounted);
GameStateSnapshot *gamestate_snapshot = nullptr;
public:
GameStateSnapshotRef(GameStateSnapshot *p_gss) :
gamestate_snapshot(p_gss) {}
bool unreference();
GameStateSnapshot *get_snapshot();
};

@ -2467,9 +2467,11 @@ void DisplayServerX11::_update_motif_wm_hints(WindowID p_window) {
WindowData &wd = windows[p_window];
MotifWmHints hints = {};
hints.flags = MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;
hints.flags = MWM_HINTS_DECORATIONS;
if (!wd.borderless) {
hints.flags |= MWM_HINTS_FUNCTIONS;
hints.decorations = MWM_DECOR_BORDER | MWM_DECOR_MENU | MWM_DECOR_TITLE;
hints.functions = MWM_FUNC_MOVE | MWM_FUNC_CLOSE;

@ -974,7 +974,8 @@ void ColorPicker::_palette_file_selected(const String &p_path) {
#endif
} break;
case FileDialog::FileMode::FILE_MODE_SAVE_FILE: {
ColorPalette *palette = memnew(ColorPalette);
Ref<ColorPalette> palette;
palette.instantiate();
palette->set_colors(get_presets());
Error error = ResourceSaver::save(palette, p_path);
ERR_FAIL_COND_MSG(error != Error::OK, vformat("Cannot open color palette file for writing at: %s", p_path));

@ -372,8 +372,8 @@ void FileDialog::_save_confirm_pressed() {
_save_to_recent();
String f = dir_access->get_current_dir().path_join(filename_edit->get_text());
emit_signal(SNAME("file_selected"), f);
hide();
emit_signal(SNAME("file_selected"), f);
}
void FileDialog::_post_popup() {
@ -413,8 +413,8 @@ void FileDialog::_action_pressed() {
const Vector<String> files = get_selected_files();
if (!files.is_empty()) {
_save_to_recent();
emit_signal(SNAME("files_selected"), files);
hide();
emit_signal(SNAME("files_selected"), files);
}
return;
}
@ -424,8 +424,8 @@ void FileDialog::_action_pressed() {
if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && (dir_access->file_exists(f) || dir_access->is_bundle(f))) {
_save_to_recent();
emit_signal(SNAME("file_selected"), f);
hide();
emit_signal(SNAME("file_selected"), f);
} else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) {
String path = dir_access->get_current_dir();
@ -439,8 +439,8 @@ void FileDialog::_action_pressed() {
}
_save_to_recent();
emit_signal(SNAME("dir_selected"), path);
hide();
emit_signal(SNAME("dir_selected"), path);
}
if (mode == FILE_MODE_SAVE_FILE) {
@ -501,15 +501,14 @@ void FileDialog::_action_pressed() {
confirm_save->popup_centered(Size2(250, 80));
} else {
_save_to_recent();
emit_signal(SNAME("file_selected"), f);
hide();
emit_signal(SNAME("file_selected"), f);
}
}
}
void FileDialog::_cancel_pressed() {
filename_edit->set_text("");
invalidate();
hide();
}
@ -534,7 +533,7 @@ bool FileDialog::_is_open_should_be_disabled() {
}
void FileDialog::_go_up() {
_change_dir("..");
_change_dir(get_current_dir().trim_suffix("/").get_base_dir());
_push_history();
}
@ -998,7 +997,7 @@ void FileDialog::update_file_list() {
thumbnail = vicon;
}
if (thumbnail.is_null()) {
thumbnail = theme_cache.file;
thumbnail = theme_cache.file_thumbnail;
}
file_list->set_item_icon(-1, thumbnail);
if (icon.is_valid()) {

@ -213,6 +213,14 @@ bool GraphElement::is_selectable() {
return selectable;
}
void GraphElement::set_scaling_menus(bool p_scaling_menus) {
scaling_menus = p_scaling_menus;
}
bool GraphElement::is_scaling_menus() const {
return scaling_menus;
}
void GraphElement::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_resizable", "resizable"), &GraphElement::set_resizable);
ClassDB::bind_method(D_METHOD("is_resizable"), &GraphElement::is_resizable);
@ -226,6 +234,9 @@ void GraphElement::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_selected", "selected"), &GraphElement::set_selected);
ClassDB::bind_method(D_METHOD("is_selected"), &GraphElement::is_selected);
ClassDB::bind_method(D_METHOD("set_scaling_menus", "scaling_menus"), &GraphElement::set_scaling_menus);
ClassDB::bind_method(D_METHOD("is_scaling_menus"), &GraphElement::is_scaling_menus);
ClassDB::bind_method(D_METHOD("set_position_offset", "offset"), &GraphElement::set_position_offset);
ClassDB::bind_method(D_METHOD("get_position_offset"), &GraphElement::get_position_offset);
@ -234,6 +245,7 @@ void GraphElement::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draggable"), "set_draggable", "is_draggable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selectable"), "set_selectable", "is_selectable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selected"), "set_selected", "is_selected");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scaling_menus"), "set_scaling_menus", "is_scaling_menus");
ADD_SIGNAL(MethodInfo("node_selected"));
ADD_SIGNAL(MethodInfo("node_deselected"));

@ -48,6 +48,8 @@ protected:
Vector2 position_offset;
bool scaling_menus = false;
struct ThemeCache {
Ref<Texture2D> resizer;
} theme_cache;
@ -84,6 +86,9 @@ public:
void set_selectable(bool p_selectable);
bool is_selectable();
void set_scaling_menus(bool p_scaling_menus);
bool is_scaling_menus() const;
virtual Size2 get_minimum_size() const override;
bool is_resizing() const {

@ -35,6 +35,7 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "scene/gui/graph_element.h"
#include "scene/gui/menu_bar.h"
#include "scene/gui/panel_container.h"
#include "scene/main/timer.h"
@ -3372,12 +3373,29 @@ void PopupMenu::_popup_base(const Rect2i &p_bounds) {
}
void PopupMenu::_pre_popup() {
Size2 scale = get_force_native() ? get_parent_viewport()->get_popup_base_transform_native().get_scale() : get_parent_viewport()->get_popup_base_transform().get_scale();
CanvasItem *c = Object::cast_to<CanvasItem>(get_parent());
if (c) {
scale *= c->get_global_transform_with_canvas().get_scale();
real_t popup_scale = 1.0;
bool scale_with_parent = true;
// Disable content scaling to avoid too tiny or too big menus when using GraphEdit zoom, applied only if menu is a child of GraphElement.
Node *p = get_parent();
while (p) {
GraphElement *ge = Object::cast_to<GraphElement>(p);
if (ge) {
scale_with_parent = ge->is_scaling_menus();
break;
}
p = p->get_parent();
}
if (scale_with_parent) {
Size2 scale = get_force_native() ? get_parent_viewport()->get_popup_base_transform_native().get_scale() : get_parent_viewport()->get_popup_base_transform().get_scale();
CanvasItem *c = Object::cast_to<CanvasItem>(get_parent());
if (c) {
scale *= c->get_global_transform_with_canvas().get_scale();
}
popup_scale = MIN(scale.x, scale.y);
}
real_t popup_scale = MIN(scale.x, scale.y);
set_content_scale_factor(popup_scale);
if (is_wrapping_controls()) {
Size2 minsize = get_contents_minimum_size() * popup_scale;

@ -615,7 +615,7 @@ RS::EnvironmentGlowBlendMode RendererEnvironmentStorage::environment_get_glow_bl
float RendererEnvironmentStorage::environment_get_glow_hdr_bleed_threshold(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL_V(env, 0.0);
ERR_FAIL_NULL_V(env, 1.0);
return env->glow_hdr_bleed_threshold;
}