summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/debugger/remote_debugger.cpp2
-rw-r--r--doc/classes/Animation.xml10
-rw-r--r--doc/classes/AudioStream.xml13
-rw-r--r--doc/classes/AudioStreamPlayback.xml15
-rw-r--r--drivers/d3d12/d3d12_context.cpp11
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp11
-rw-r--r--editor/editor_dock_manager.cpp716
-rw-r--r--editor/editor_dock_manager.h135
-rw-r--r--editor/editor_node.cpp800
-rw-r--r--editor/editor_node.h71
-rw-r--r--editor/editor_plugin.cpp5
-rw-r--r--editor/editor_properties_array_dict.cpp4
-rw-r--r--editor/filesystem_dock.cpp64
-rw-r--r--editor/filesystem_dock.h9
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp5
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp29
-rw-r--r--modules/minimp3/audio_stream_mp3.h7
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp32
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h7
-rw-r--r--platform/android/export/export_plugin.cpp6
-rw-r--r--scene/2d/audio_stream_player_2d.cpp69
-rw-r--r--scene/2d/audio_stream_player_2d.h12
-rw-r--r--scene/3d/audio_stream_player_3d.cpp68
-rw-r--r--scene/3d/audio_stream_player_3d.h12
-rw-r--r--scene/audio/audio_stream_player.cpp68
-rw-r--r--scene/audio/audio_stream_player.h12
-rw-r--r--scene/gui/split_container.cpp18
-rw-r--r--scene/gui/split_container.h4
-rw-r--r--servers/audio/audio_stream.cpp25
-rw-r--r--servers/audio/audio_stream.h19
-rw-r--r--servers/rendering/shader_compiler.cpp2
31 files changed, 1451 insertions, 810 deletions
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index a817ea871d..ce675d6b06 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -435,9 +435,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
messages.insert(Thread::get_caller_id(), List<Message>());
}
- mutex.lock();
while (is_peer_connected()) {
- mutex.unlock();
flush_output();
_poll_messages();
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index 15f6b16439..2a88e70818 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -8,21 +8,23 @@
[codeblocks]
[gdscript]
# This creates an animation that makes the node "Enemy" move to the right by
- # 100 pixels in 0.5 seconds.
+ # 100 pixels in 2.0 seconds.
var animation = Animation.new()
var track_index = animation.add_track(Animation.TYPE_VALUE)
animation.track_set_path(track_index, "Enemy:position:x")
animation.track_insert_key(track_index, 0.0, 0)
- animation.track_insert_key(track_index, 0.5, 100)
+ animation.track_insert_key(track_index, 2.0, 100)
+ animation.length = 2.0
[/gdscript]
[csharp]
// This creates an animation that makes the node "Enemy" move to the right by
- // 100 pixels in 0.5 seconds.
+ // 100 pixels in 2.0 seconds.
var animation = new Animation();
int trackIndex = animation.AddTrack(Animation.TrackType.Value);
animation.TrackSetPath(trackIndex, "Enemy:position:x");
animation.TrackInsertKey(trackIndex, 0.0f, 0);
- animation.TrackInsertKey(trackIndex, 0.5f, 100);
+ animation.TrackInsertKey(trackIndex, 2.0f, 100);
+ animation.Length = 2.0f;
[/csharp]
[/codeblocks]
Animations are just data containers, and must be added to nodes such as an [AnimationPlayer] to be played back. Animation tracks have different types, each with its own set of dedicated methods. Check [enum TrackType] to see available types.
diff --git a/doc/classes/AudioStream.xml b/doc/classes/AudioStream.xml
index 12e09b235f..6e30775fee 100644
--- a/doc/classes/AudioStream.xml
+++ b/doc/classes/AudioStream.xml
@@ -28,6 +28,12 @@
<description>
</description>
</method>
+ <method name="_get_parameter_list" qualifiers="virtual const">
+ <return type="Dictionary[]" />
+ <description>
+ Return the controllable parameters of this stream. This array contains dictionaries with a property info description format (see [method Object.get_property_list]). Additionally, the default value for this parameter must be added tho each dictionary in "default_value" field.
+ </description>
+ </method>
<method name="_get_stream_name" qualifiers="virtual const">
<return type="String" />
<description>
@@ -62,4 +68,11 @@
</description>
</method>
</methods>
+ <signals>
+ <signal name="parameter_list_changed">
+ <description>
+ Signal to be emitted to notify when the parameter list changed.
+ </description>
+ </signal>
+ </signals>
</class>
diff --git a/doc/classes/AudioStreamPlayback.xml b/doc/classes/AudioStreamPlayback.xml
index 7692690b5e..a090989194 100644
--- a/doc/classes/AudioStreamPlayback.xml
+++ b/doc/classes/AudioStreamPlayback.xml
@@ -15,6 +15,13 @@
<description>
</description>
</method>
+ <method name="_get_parameter" qualifiers="virtual const">
+ <return type="Variant" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Return the current value of a playback parameter by name (see [method AudioStream._get_parameter_list]).
+ </description>
+ </method>
<method name="_get_playback_position" qualifiers="virtual const">
<return type="float" />
<description>
@@ -39,6 +46,14 @@
<description>
</description>
</method>
+ <method name="_set_parameter" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" />
+ <param index="1" name="value" type="Variant" />
+ <description>
+ Set the current value of a playback parameter by name (see [method AudioStream._get_parameter_list]).
+ </description>
+ </method>
<method name="_start" qualifiers="virtual">
<return type="void" />
<param index="0" name="from_pos" type="float" />
diff --git a/drivers/d3d12/d3d12_context.cpp b/drivers/d3d12/d3d12_context.cpp
index 37066a811d..0ba3f59119 100644
--- a/drivers/d3d12/d3d12_context.cpp
+++ b/drivers/d3d12/d3d12_context.cpp
@@ -55,12 +55,11 @@
#include <guiddef.h>
#include <dxguids.h>
-#ifndef CLSID_D3D12DeviceFactory
-// Note: symbol is not available in MinGW import library.
-const CLSID CLSID_D3D12DeviceFactory = __uuidof(ID3D12DeviceFactory);
-#endif
#endif
+// Note: symbol is not available in MinGW and old MSVC import libraries.
+const CLSID CLSID_D3D12DeviceFactoryGodot = __uuidof(ID3D12DeviceFactory);
+
extern "C" {
char godot_nir_arch_name[32];
}
@@ -825,9 +824,9 @@ void D3D12Context::_init_device_factory() {
ID3D12SDKConfiguration1 *sdk_config1 = nullptr;
if (SUCCEEDED(sdk_config->QueryInterface(&sdk_config1))) {
if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, agility_sdk_path.ascii().get_data(), IID_PPV_ARGS(device_factory.GetAddressOf())))) {
- d3d_D3D12GetInterface(CLSID_D3D12DeviceFactory, IID_PPV_ARGS(device_factory.GetAddressOf()));
+ d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf()));
} else if (SUCCEEDED(sdk_config1->CreateDeviceFactory(agility_sdk_version, ".\\", IID_PPV_ARGS(device_factory.GetAddressOf())))) {
- d3d_D3D12GetInterface(CLSID_D3D12DeviceFactory, IID_PPV_ARGS(device_factory.GetAddressOf()));
+ d3d_D3D12GetInterface(CLSID_D3D12DeviceFactoryGodot, IID_PPV_ARGS(device_factory.GetAddressOf()));
}
sdk_config1->Release();
}
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index d3a7344c9d..1d1dc6bec8 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -78,6 +78,17 @@ extern "C" {
#undef UNUSED
#endif
+#ifdef PIX_ENABLED
+#if defined(__GNUC__)
+#define _MSC_VER 1800
+#endif
+#define USE_PIX
+#include "WinPixEventRuntime/pix3.h"
+#if defined(__GNUC__)
+#undef _MSC_VER
+#endif
+#endif
+
static const D3D12_RANGE VOID_RANGE = {};
static const uint32_t ROOT_CONSTANT_SPACE = RDD::MAX_UNIFORM_SETS + 1;
diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp
new file mode 100644
index 0000000000..220ee8fc41
--- /dev/null
+++ b/editor/editor_dock_manager.cpp
@@ -0,0 +1,716 @@
+/**************************************************************************/
+/* editor_dock_manager.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_dock_manager.h"
+
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/label.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tab_container.h"
+#include "scene/main/window.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
+#include "editor/filesystem_dock.h"
+#include "editor/window_wrapper.h"
+
+EditorDockManager *EditorDockManager::singleton = nullptr;
+
+void DockSplitContainer::_update_visibility() {
+ if (is_updating) {
+ return;
+ }
+ is_updating = true;
+ bool any_visible = false;
+ for (int i = 0; i < 2; i++) {
+ Control *split = get_containable_child(i);
+ if (split && split->is_visible()) {
+ any_visible = true;
+ break;
+ }
+ }
+ set_visible(any_visible);
+ is_updating = false;
+}
+
+void DockSplitContainer::add_child_notify(Node *p_child) {
+ SplitContainer::add_child_notify(p_child);
+
+ Control *child_control = nullptr;
+ for (int i = 0; i < 2; i++) {
+ Control *split = get_containable_child(i);
+ if (p_child == split) {
+ child_control = split;
+ break;
+ }
+ }
+ if (!child_control) {
+ return;
+ }
+
+ child_control->connect("visibility_changed", callable_mp(this, &DockSplitContainer::_update_visibility));
+ _update_visibility();
+}
+
+void DockSplitContainer::remove_child_notify(Node *p_child) {
+ SplitContainer::remove_child_notify(p_child);
+
+ Control *child_control = nullptr;
+ for (int i = 0; i < 2; i++) {
+ Control *split = get_containable_child(i);
+ if (p_child == split) {
+ child_control = split;
+ break;
+ }
+ }
+ if (!child_control) {
+ return;
+ }
+
+ child_control->disconnect("visibility_changed", callable_mp(this, &DockSplitContainer::_update_visibility));
+ _update_visibility();
+}
+
+void EditorDockManager::_dock_select_popup_theme_changed() {
+ if (dock_float) {
+ dock_float->set_icon(dock_select_popup->get_editor_theme_icon(SNAME("MakeFloating")));
+ }
+ if (dock_select_popup->is_layout_rtl()) {
+ dock_tab_move_left->set_icon(dock_select_popup->get_editor_theme_icon(SNAME("Forward")));
+ dock_tab_move_right->set_icon(dock_select_popup->get_editor_theme_icon(SNAME("Back")));
+ } else {
+ dock_tab_move_left->set_icon(dock_select_popup->get_editor_theme_icon(SNAME("Back")));
+ dock_tab_move_right->set_icon(dock_select_popup->get_editor_theme_icon(SNAME("Forward")));
+ }
+}
+
+void EditorDockManager::_dock_popup_exit() {
+ dock_select_rect_over_idx = -1;
+ dock_select->queue_redraw();
+}
+
+void EditorDockManager::_dock_pre_popup(int p_dock_slot) {
+ dock_popup_selected_idx = p_dock_slot;
+}
+
+void EditorDockManager::_dock_move_left() {
+ if (dock_popup_selected_idx < 0 || dock_popup_selected_idx >= DOCK_SLOT_MAX) {
+ return;
+ }
+ Control *current_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab());
+ Control *prev_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab() - 1);
+ if (!current_ctl || !prev_ctl) {
+ return;
+ }
+ dock_slot[dock_popup_selected_idx]->move_child(current_ctl, prev_ctl->get_index(false));
+ dock_select->queue_redraw();
+ _edit_current();
+ emit_signal(SNAME("layout_changed"));
+}
+
+void EditorDockManager::_dock_move_right() {
+ if (dock_popup_selected_idx < 0 || dock_popup_selected_idx >= DOCK_SLOT_MAX) {
+ return;
+ }
+ Control *current_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab());
+ Control *next_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab() + 1);
+ if (!current_ctl || !next_ctl) {
+ return;
+ }
+ dock_slot[dock_popup_selected_idx]->move_child(next_ctl, current_ctl->get_index(false));
+ dock_select->queue_redraw();
+ _edit_current();
+ emit_signal(SNAME("layout_changed"));
+}
+
+void EditorDockManager::_dock_select_input(const Ref<InputEvent> &p_input) {
+ Ref<InputEventMouse> me = p_input;
+
+ if (me.is_valid()) {
+ Vector2 point = me->get_position();
+
+ int nrect = -1;
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ if (dock_select_rect[i].has_point(point)) {
+ nrect = i;
+ break;
+ }
+ }
+
+ if (nrect != dock_select_rect_over_idx) {
+ dock_select->queue_redraw();
+ dock_select_rect_over_idx = nrect;
+ }
+
+ if (nrect == -1) {
+ return;
+ }
+
+ Ref<InputEventMouseButton> mb = me;
+
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed() && dock_popup_selected_idx != nrect) {
+ dock_slot[nrect]->move_tab_from_tab_container(dock_slot[dock_popup_selected_idx], dock_slot[dock_popup_selected_idx]->get_current_tab(), dock_slot[nrect]->get_tab_count());
+
+ if (dock_slot[dock_popup_selected_idx]->get_tab_count() == 0) {
+ dock_slot[dock_popup_selected_idx]->hide();
+ } else {
+ dock_slot[dock_popup_selected_idx]->set_current_tab(0);
+ }
+
+ dock_popup_selected_idx = nrect;
+ dock_slot[nrect]->show();
+ dock_select->queue_redraw();
+
+ update_dock_slots_visibility(true);
+
+ _edit_current();
+ emit_signal(SNAME("layout_changed"));
+ }
+ }
+}
+
+void EditorDockManager::_dock_select_draw() {
+ Size2 s = dock_select->get_size();
+ s.y /= 2.0;
+ s.x /= 6.0;
+
+ Color used = Color(0.6, 0.6, 0.6, 0.8);
+ Color used_selected = Color(0.8, 0.8, 0.8, 0.8);
+ Color tab_selected = dock_select->get_theme_color(SNAME("mono_color"), EditorStringName(Editor));
+ Color unused = used;
+ unused.a = 0.4;
+ Color unusable = unused;
+ unusable.a = 0.1;
+
+ Rect2 unr(s.x * 2, 0, s.x * 2, s.y * 2);
+ unr.position += Vector2(2, 5);
+ unr.size -= Vector2(4, 7);
+
+ dock_select->draw_rect(unr, unusable);
+
+ dock_tab_move_left->set_disabled(true);
+ dock_tab_move_right->set_disabled(true);
+
+ if (dock_popup_selected_idx != -1 && dock_slot[dock_popup_selected_idx]->get_tab_count()) {
+ dock_tab_move_left->set_disabled(dock_slot[dock_popup_selected_idx]->get_current_tab() == 0);
+ dock_tab_move_right->set_disabled(dock_slot[dock_popup_selected_idx]->get_current_tab() >= dock_slot[dock_popup_selected_idx]->get_tab_count() - 1);
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ Vector2 ofs;
+
+ switch (i) {
+ case DOCK_SLOT_LEFT_UL: {
+ } break;
+ case DOCK_SLOT_LEFT_BL: {
+ ofs.y += s.y;
+ } break;
+ case DOCK_SLOT_LEFT_UR: {
+ ofs.x += s.x;
+ } break;
+ case DOCK_SLOT_LEFT_BR: {
+ ofs += s;
+ } break;
+ case DOCK_SLOT_RIGHT_UL: {
+ ofs.x += s.x * 4;
+ } break;
+ case DOCK_SLOT_RIGHT_BL: {
+ ofs.x += s.x * 4;
+ ofs.y += s.y;
+
+ } break;
+ case DOCK_SLOT_RIGHT_UR: {
+ ofs.x += s.x * 4;
+ ofs.x += s.x;
+
+ } break;
+ case DOCK_SLOT_RIGHT_BR: {
+ ofs.x += s.x * 4;
+ ofs += s;
+
+ } break;
+ }
+
+ Rect2 r(ofs, s);
+ dock_select_rect[i] = r;
+ r.position += Vector2(2, 5);
+ r.size -= Vector2(4, 7);
+
+ if (i == dock_select_rect_over_idx) {
+ dock_select->draw_rect(r, used_selected);
+ } else if (dock_slot[i]->get_tab_count() == 0) {
+ dock_select->draw_rect(r, unused);
+ } else {
+ dock_select->draw_rect(r, used);
+ }
+
+ for (int j = 0; j < MIN(3, dock_slot[i]->get_tab_count()); j++) {
+ int xofs = (r.size.width / 3) * j;
+ Color c = used;
+ if (i == dock_popup_selected_idx && (dock_slot[i]->get_current_tab() > 3 || dock_slot[i]->get_current_tab() == j)) {
+ c = tab_selected;
+ }
+ dock_select->draw_rect(Rect2(2 + ofs.x + xofs, ofs.y, r.size.width / 3 - 1, 3), c);
+ }
+ }
+}
+
+void EditorDockManager::_dock_split_dragged(int p_offset) {
+ EditorNode::get_singleton()->save_editor_layout_delayed();
+}
+
+void EditorDockManager::_dock_tab_changed(int p_tab) {
+ // Update visibility but don't set current tab.
+ update_dock_slots_visibility(true);
+}
+
+void EditorDockManager::_edit_current() {
+ EditorNode::get_singleton()->edit_current();
+}
+
+void EditorDockManager::_dock_floating_close_request(WindowWrapper *p_wrapper) {
+ int dock_slot_num = p_wrapper->get_meta("dock_slot");
+ int dock_slot_index = p_wrapper->get_meta("dock_index");
+
+ // Give back the dock to the original owner.
+ Control *dock = p_wrapper->release_wrapped_control();
+
+ int target_index = MIN(dock_slot_index, dock_slot[dock_slot_num]->get_tab_count());
+ dock_slot[dock_slot_num]->add_child(dock);
+ dock_slot[dock_slot_num]->move_child(dock, target_index);
+ dock_slot[dock_slot_num]->set_current_tab(target_index);
+
+ floating_docks.erase(p_wrapper);
+ p_wrapper->queue_free();
+
+ update_dock_slots_visibility(true);
+
+ _edit_current();
+}
+
+void EditorDockManager::_dock_make_selected_float() {
+ Control *dock = dock_slot[dock_popup_selected_idx]->get_current_tab_control();
+ _dock_make_float(dock, dock_popup_selected_idx);
+
+ dock_select_popup->hide();
+ _edit_current();
+}
+
+void EditorDockManager::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show_window) {
+ ERR_FAIL_NULL(p_dock);
+
+ Size2 borders = Size2(4, 4) * EDSCALE;
+ // Remember size and position before removing it from the main window.
+ Size2 dock_size = p_dock->get_size() + borders * 2;
+ Point2 dock_screen_pos = p_dock->get_screen_position();
+
+ int dock_index = p_dock->get_index() - 1;
+ dock_slot[p_slot_index]->remove_child(p_dock);
+
+ WindowWrapper *wrapper = memnew(WindowWrapper);
+ wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), p_dock->get_name()));
+ wrapper->set_margins_enabled(true);
+
+ EditorNode::get_singleton()->get_gui_base()->add_child(wrapper);
+
+ wrapper->set_wrapped_control(p_dock);
+ wrapper->set_meta("dock_slot", p_slot_index);
+ wrapper->set_meta("dock_index", dock_index);
+ wrapper->set_meta("dock_name", p_dock->get_name().operator String());
+ p_dock->show();
+
+ wrapper->connect("window_close_requested", callable_mp(this, &EditorDockManager::_dock_floating_close_request).bind(wrapper));
+
+ dock_select_popup->hide();
+
+ if (p_show_window) {
+ wrapper->restore_window(Rect2i(dock_screen_pos, dock_size), EditorNode::get_singleton()->get_gui_base()->get_window()->get_current_screen());
+ }
+
+ update_dock_slots_visibility(true);
+
+ floating_docks.push_back(wrapper);
+
+ _edit_current();
+}
+
+void EditorDockManager::_restore_floating_dock(const Dictionary &p_dock_dump, Control *p_dock, int p_slot_index) {
+ WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(p_dock);
+ if (!wrapper) {
+ _dock_make_float(p_dock, p_slot_index, false);
+ wrapper = floating_docks[floating_docks.size() - 1];
+ }
+
+ wrapper->restore_window_from_saved_position(
+ p_dock_dump.get("window_rect", Rect2i()),
+ p_dock_dump.get("window_screen", -1),
+ p_dock_dump.get("window_screen_rect", Rect2i()));
+}
+
+void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ String names;
+ for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) {
+ String name = dock_slot[i]->get_tab_control(j)->get_name();
+ if (!names.is_empty()) {
+ names += ",";
+ }
+ names += name;
+ }
+
+ String config_key = "dock_" + itos(i + 1);
+
+ if (p_layout->has_section_key(p_section, config_key)) {
+ p_layout->erase_section_key(p_section, config_key);
+ }
+
+ if (!names.is_empty()) {
+ p_layout->set_value(p_section, config_key, names);
+ }
+
+ int selected_tab_idx = dock_slot[i]->get_current_tab();
+ if (selected_tab_idx >= 0) {
+ p_layout->set_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx", selected_tab_idx);
+ }
+ }
+
+ Dictionary floating_docks_dump;
+
+ for (WindowWrapper *wrapper : floating_docks) {
+ Control *dock = wrapper->get_wrapped_control();
+
+ Dictionary dock_dump;
+ dock_dump["window_rect"] = wrapper->get_window_rect();
+
+ int screen = wrapper->get_window_screen();
+ dock_dump["window_screen"] = wrapper->get_window_screen();
+ dock_dump["window_screen_rect"] = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
+
+ String name = dock->get_name();
+ floating_docks_dump[name] = dock_dump;
+
+ int dock_slot_id = wrapper->get_meta("dock_slot");
+ String config_key = "dock_" + itos(dock_slot_id + 1);
+
+ String names = p_layout->get_value(p_section, config_key, "");
+ if (names.is_empty()) {
+ names = name;
+ } else {
+ names += "," + name;
+ }
+ p_layout->set_value(p_section, config_key, names);
+ }
+
+ p_layout->set_value(p_section, "dock_floating", floating_docks_dump);
+
+ for (int i = 0; i < vsplits.size(); i++) {
+ if (vsplits[i]->is_visible_in_tree()) {
+ p_layout->set_value(p_section, "dock_split_" + itos(i + 1), vsplits[i]->get_split_offset());
+ }
+ }
+
+ for (int i = 0; i < hsplits.size(); i++) {
+ p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset());
+ }
+
+ 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) {
+ Dictionary floating_docks_dump = p_layout->get_value(p_section, "dock_floating", Dictionary());
+
+ bool restore_window_on_load = EDITOR_GET("interface/multi_window/restore_windows_on_load");
+
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1))) {
+ continue;
+ }
+
+ Vector<String> names = String(p_layout->get_value(p_section, "dock_" + itos(i + 1))).split(",");
+
+ for (int j = names.size() - 1; j >= 0; j--) {
+ String name = names[j];
+
+ // FIXME: Find it, in a horribly inefficient way.
+ int atidx = -1;
+ Control *node = nullptr;
+ for (int k = 0; k < DOCK_SLOT_MAX; k++) {
+ if (!dock_slot[k]->has_node(name)) {
+ continue;
+ }
+ node = Object::cast_to<Control>(dock_slot[k]->get_node(name));
+ if (!node) {
+ continue;
+ }
+ atidx = k;
+ break;
+ }
+
+ if (atidx == -1) {
+ // Try floating docks.
+ for (WindowWrapper *wrapper : floating_docks) {
+ if (wrapper->get_meta("dock_name") == name) {
+ if (restore_window_on_load && floating_docks_dump.has(name)) {
+ _restore_floating_dock(floating_docks_dump[name], wrapper, i);
+ } else {
+ atidx = wrapper->get_meta("dock_slot");
+ node = wrapper->get_wrapped_control();
+ wrapper->set_window_enabled(false);
+ }
+ break;
+ }
+ }
+ }
+ if (!node) {
+ // Well, it's not anywhere.
+ continue;
+ }
+
+ if (atidx == i) {
+ dock_slot[i]->move_child(node, 0);
+ } else if (atidx != -1) {
+ dock_slot[i]->set_block_signals(true);
+ dock_slot[atidx]->set_block_signals(true);
+ dock_slot[i]->move_tab_from_tab_container(dock_slot[atidx], dock_slot[atidx]->get_tab_idx_from_control(node), 0);
+ dock_slot[i]->set_block_signals(false);
+ dock_slot[atidx]->set_block_signals(false);
+ }
+
+ WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(node);
+ if (restore_window_on_load && floating_docks_dump.has(name)) {
+ if (!dock_slot[i]->is_tab_hidden(dock_slot[i]->get_tab_idx_from_control(node))) {
+ _restore_floating_dock(floating_docks_dump[name], node, i);
+ }
+ } else if (wrapper) {
+ wrapper->set_window_enabled(false);
+ }
+ }
+
+ if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx")) {
+ continue;
+ }
+
+ int selected_tab_idx = p_layout->get_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx");
+ if (selected_tab_idx >= 0 && selected_tab_idx < dock_slot[i]->get_tab_count()) {
+ callable_mp(dock_slot[i], &TabContainer::set_current_tab).call_deferred(selected_tab_idx);
+ }
+ }
+
+ 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;
+ }
+ int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
+ hsplits[i]->set_split_offset(ofs);
+ }
+
+ update_dock_slots_visibility(false);
+
+ FileSystemDock::get_singleton()->load_layout_from_config(p_layout, p_section);
+}
+
+void EditorDockManager::update_dock_slots_visibility(bool p_keep_selected_tabs) {
+ if (!docks_visible) {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ dock_slot[i]->hide();
+ }
+ } else {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ int first_tab_visible = -1;
+ for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) {
+ if (!dock_slot[i]->is_tab_hidden(j)) {
+ first_tab_visible = j;
+ break;
+ }
+ }
+ if (first_tab_visible >= 0) {
+ dock_slot[i]->show();
+ if (p_keep_selected_tabs) {
+ int current_tab = dock_slot[i]->get_current_tab();
+ if (dock_slot[i]->is_tab_hidden(current_tab)) {
+ dock_slot[i]->set_block_signals(true);
+ dock_slot[i]->select_next_available();
+ dock_slot[i]->set_block_signals(false);
+ }
+ } else {
+ dock_slot[i]->set_block_signals(true);
+ dock_slot[i]->set_current_tab(first_tab_visible);
+ dock_slot[i]->set_block_signals(false);
+ }
+ } else {
+ dock_slot[i]->hide();
+ }
+ }
+ }
+}
+
+void EditorDockManager::close_all_floating_docks() {
+ for (WindowWrapper *wrapper : floating_docks) {
+ wrapper->set_window_enabled(false);
+ }
+}
+
+void EditorDockManager::add_control_to_dock(DockSlot p_slot, Control *p_control, const String &p_name) {
+ ERR_FAIL_INDEX(p_slot, DOCK_SLOT_MAX);
+ dock_slot[p_slot]->add_child(p_control);
+ if (!p_name.is_empty()) {
+ dock_slot[p_slot]->set_tab_title(dock_slot[p_slot]->get_tab_idx_from_control(p_control), p_name);
+ }
+}
+
+void EditorDockManager::remove_control_from_dock(Control *p_control) {
+ // If the dock is floating, close it first.
+ for (WindowWrapper *wrapper : floating_docks) {
+ if (p_control == wrapper->get_wrapped_control()) {
+ wrapper->set_window_enabled(false);
+ break;
+ }
+ }
+
+ Control *dock = nullptr;
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ if (p_control->get_parent() == dock_slot[i]) {
+ dock = dock_slot[i];
+ break;
+ }
+ }
+
+ ERR_FAIL_NULL_MSG(dock, "Control is not in a dock.");
+
+ dock->remove_child(p_control);
+ update_dock_slots_visibility();
+}
+
+void EditorDockManager::set_docks_visible(bool p_show) {
+ docks_visible = p_show;
+ update_dock_slots_visibility(true);
+}
+
+bool EditorDockManager::are_docks_visible() const {
+ return docks_visible;
+}
+
+void EditorDockManager::add_vsplit(DockSplitContainer *p_split) {
+ vsplits.push_back(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);
+ p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged));
+}
+
+void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container) {
+ ERR_FAIL_NULL(p_tab_container);
+ ERR_FAIL_INDEX(p_dock_slot, DOCK_SLOT_MAX);
+
+ dock_slot[p_dock_slot] = p_tab_container;
+
+ p_tab_container->set_custom_minimum_size(Size2(170, 0) * EDSCALE);
+ p_tab_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ p_tab_container->set_popup(dock_select_popup);
+ p_tab_container->connect("pre_popup_pressed", callable_mp(this, &EditorDockManager::_dock_pre_popup).bind(p_dock_slot));
+ p_tab_container->set_drag_to_rearrange_enabled(true);
+ p_tab_container->set_tabs_rearrange_group(1);
+ p_tab_container->connect("tab_changed", callable_mp(this, &EditorDockManager::_dock_tab_changed));
+ p_tab_container->set_use_hidden_tabs_for_min_size(true);
+}
+
+int EditorDockManager::get_vsplit_count() const {
+ return vsplits.size();
+}
+
+void EditorDockManager::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("layout_changed"));
+}
+
+EditorDockManager::EditorDockManager() {
+ singleton = this;
+
+ dock_select_popup = memnew(PopupPanel);
+ EditorNode::get_singleton()->get_gui_base()->add_child(dock_select_popup);
+ VBoxContainer *dock_vb = memnew(VBoxContainer);
+ dock_select_popup->add_child(dock_vb);
+ dock_select_popup->connect("theme_changed", callable_mp(this, &EditorDockManager::_dock_select_popup_theme_changed));
+
+ HBoxContainer *dock_hb = memnew(HBoxContainer);
+ dock_tab_move_left = memnew(Button);
+ dock_tab_move_left->set_flat(true);
+ dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE);
+ dock_tab_move_left->connect("pressed", callable_mp(this, &EditorDockManager::_dock_move_left));
+ dock_hb->add_child(dock_tab_move_left);
+
+ Label *dock_label = memnew(Label);
+ dock_label->set_text(TTR("Dock Position"));
+ dock_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ dock_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ dock_hb->add_child(dock_label);
+
+ dock_tab_move_right = memnew(Button);
+ dock_tab_move_right->set_flat(true);
+ dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE);
+ dock_tab_move_right->connect("pressed", callable_mp(this, &EditorDockManager::_dock_move_right));
+
+ dock_hb->add_child(dock_tab_move_right);
+ dock_vb->add_child(dock_hb);
+
+ dock_select = memnew(Control);
+ dock_select->set_custom_minimum_size(Size2(128, 64) * EDSCALE);
+ dock_select->connect("gui_input", callable_mp(this, &EditorDockManager::_dock_select_input));
+ dock_select->connect("draw", callable_mp(this, &EditorDockManager::_dock_select_draw));
+ dock_select->connect("mouse_exited", callable_mp(this, &EditorDockManager::_dock_popup_exit));
+ dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ dock_vb->add_child(dock_select);
+
+ if (!SceneTree::get_singleton()->get_root()->is_embedding_subwindows() && !EDITOR_GET("interface/editor/single_window_mode") && EDITOR_GET("interface/multi_window/enable")) {
+ dock_float = memnew(Button);
+ dock_float->set_text(TTR("Make Floating"));
+ dock_float->set_focus_mode(Control::FOCUS_NONE);
+ dock_float->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
+ dock_float->connect("pressed", callable_mp(this, &EditorDockManager::_dock_make_selected_float));
+
+ dock_vb->add_child(dock_float);
+ }
+
+ dock_select_popup->reset_size();
+}
diff --git a/editor/editor_dock_manager.h b/editor/editor_dock_manager.h
new file mode 100644
index 0000000000..e685fe1380
--- /dev/null
+++ b/editor/editor_dock_manager.h
@@ -0,0 +1,135 @@
+/**************************************************************************/
+/* editor_dock_manager.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_DOCK_MANAGER_H
+#define EDITOR_DOCK_MANAGER_H
+
+#include "scene/gui/split_container.h"
+
+class Button;
+class ConfigFile;
+class Control;
+class PopupPanel;
+class TabContainer;
+class WindowWrapper;
+
+class DockSplitContainer : public SplitContainer {
+ GDCLASS(DockSplitContainer, SplitContainer);
+
+private:
+ bool is_updating = false;
+
+protected:
+ void _update_visibility();
+
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
+};
+
+class EditorDockManager : public Object {
+ GDCLASS(EditorDockManager, Object);
+
+public:
+ enum DockSlot {
+ DOCK_SLOT_LEFT_UL,
+ DOCK_SLOT_LEFT_BL,
+ DOCK_SLOT_LEFT_UR,
+ DOCK_SLOT_LEFT_BR,
+ DOCK_SLOT_RIGHT_UL,
+ DOCK_SLOT_RIGHT_BL,
+ DOCK_SLOT_RIGHT_UR,
+ DOCK_SLOT_RIGHT_BR,
+ DOCK_SLOT_MAX
+ };
+
+private:
+ static EditorDockManager *singleton;
+
+ // To access splits easily by index.
+ Vector<DockSplitContainer *> vsplits;
+ Vector<DockSplitContainer *> hsplits;
+
+ Vector<WindowWrapper *> floating_docks;
+ TabContainer *dock_slot[DOCK_SLOT_MAX];
+ bool docks_visible = true;
+
+ PopupPanel *dock_select_popup = nullptr;
+ Button *dock_float = nullptr;
+ Button *dock_tab_move_left = nullptr;
+ Button *dock_tab_move_right = nullptr;
+ Control *dock_select = nullptr;
+ Rect2 dock_select_rect[DOCK_SLOT_MAX];
+ int dock_select_rect_over_idx = -1;
+ int dock_popup_selected_idx = -1;
+
+ void _dock_select_popup_theme_changed();
+ void _dock_popup_exit();
+ void _dock_pre_popup(int p_dock_slot);
+ void _dock_move_left();
+ void _dock_move_right();
+ void _dock_select_input(const Ref<InputEvent> &p_input);
+ void _dock_select_draw();
+ void _dock_split_dragged(int p_offset);
+
+ void _dock_tab_changed(int p_tab);
+ void _edit_current();
+
+ void _dock_floating_close_request(WindowWrapper *p_wrapper);
+ void _dock_make_selected_float();
+ void _dock_make_float(Control *p_control, int p_slot_index, bool p_show_window = true);
+ void _restore_floating_dock(const Dictionary &p_dock_dump, Control *p_wrapper, int p_slot_index);
+
+protected:
+ static void _bind_methods();
+
+public:
+ static EditorDockManager *get_singleton() { return singleton; }
+
+ void add_vsplit(DockSplitContainer *p_split);
+ void add_hsplit(DockSplitContainer *p_split);
+ void register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container);
+ int get_vsplit_count() const;
+
+ 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 update_dock_slots_visibility(bool p_keep_selected_tabs = false);
+
+ void close_all_floating_docks();
+
+ void set_docks_visible(bool p_show);
+ bool are_docks_visible() const;
+
+ void add_control_to_dock(DockSlot p_slot, Control *p_control, const String &p_name = "");
+ void remove_control_from_dock(Control *p_control);
+
+ EditorDockManager();
+};
+
+#endif // EDITOR_DOCK_MANAGER_H
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index fd1d598f90..d201781c23 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -79,6 +79,7 @@
#include "editor/editor_build_profile.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_data.h"
+#include "editor/editor_dock_manager.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_folding.h"
#include "editor/editor_help.h"
@@ -507,14 +508,6 @@ void EditorNode::_update_theme(bool p_skip_creation) {
distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons)));
- if (gui_base->is_layout_rtl()) {
- dock_tab_move_left->set_icon(theme->get_icon(SNAME("Forward"), EditorStringName(EditorIcons)));
- dock_tab_move_right->set_icon(theme->get_icon(SNAME("Back"), EditorStringName(EditorIcons)));
- } else {
- dock_tab_move_left->set_icon(theme->get_icon(SNAME("Back"), EditorStringName(EditorIcons)));
- dock_tab_move_right->set_icon(theme->get_icon(SNAME("Forward"), EditorStringName(EditorIcons)));
- }
-
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
@@ -2095,7 +2088,7 @@ void EditorNode::_dialog_action(String p_file) {
return;
}
- _save_docks_to_config(config, p_file);
+ editor_dock_manager->save_docks_to_config(config, p_file);
config->save(EditorSettings::get_singleton()->get_editor_layouts_config());
@@ -4738,240 +4731,6 @@ void EditorNode::_copy_warning(const String &p_str) {
DisplayServer::get_singleton()->clipboard_set(warning->get_text());
}
-void EditorNode::_dock_floating_close_request(WindowWrapper *p_wrapper) {
- int dock_slot_num = p_wrapper->get_meta("dock_slot");
- int dock_slot_index = p_wrapper->get_meta("dock_index");
-
- // Give back the dock to the original owner.
- Control *dock = p_wrapper->release_wrapped_control();
-
- int target_index = MIN(dock_slot_index, dock_slot[dock_slot_num]->get_tab_count());
- dock_slot[dock_slot_num]->add_child(dock);
- dock_slot[dock_slot_num]->move_child(dock, target_index);
- dock_slot[dock_slot_num]->set_current_tab(target_index);
-
- floating_docks.erase(p_wrapper);
- p_wrapper->queue_free();
-
- _update_dock_slots_visibility(true);
-
- _edit_current();
-}
-
-void EditorNode::_dock_make_selected_float() {
- Control *dock = dock_slot[dock_popup_selected_idx]->get_current_tab_control();
- _dock_make_float(dock, dock_popup_selected_idx);
-
- dock_select_popup->hide();
- _edit_current();
-}
-
-void EditorNode::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show_window) {
- ERR_FAIL_NULL(p_dock);
-
- Size2 borders = Size2(4, 4) * EDSCALE;
- // Remember size and position before removing it from the main window.
- Size2 dock_size = p_dock->get_size() + borders * 2;
- Point2 dock_screen_pos = p_dock->get_screen_position();
-
- int dock_index = p_dock->get_index() - 1;
- dock_slot[p_slot_index]->remove_child(p_dock);
-
- WindowWrapper *wrapper = memnew(WindowWrapper);
- wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), p_dock->get_name()));
- wrapper->set_margins_enabled(true);
-
- gui_base->add_child(wrapper);
-
- wrapper->set_wrapped_control(p_dock);
- wrapper->set_meta("dock_slot", p_slot_index);
- wrapper->set_meta("dock_index", dock_index);
- wrapper->set_meta("dock_name", p_dock->get_name().operator String());
- p_dock->show();
-
- wrapper->connect("window_close_requested", callable_mp(this, &EditorNode::_dock_floating_close_request).bind(wrapper));
-
- dock_select_popup->hide();
-
- if (p_show_window) {
- wrapper->restore_window(Rect2i(dock_screen_pos, dock_size), get_window()->get_current_screen());
- }
-
- _update_dock_slots_visibility(true);
-
- floating_docks.push_back(wrapper);
-
- _edit_current();
-}
-
-void EditorNode::_dock_select_input(const Ref<InputEvent> &p_input) {
- Ref<InputEventMouse> me = p_input;
-
- if (me.is_valid()) {
- Vector2 point = me->get_position();
-
- int nrect = -1;
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- if (dock_select_rect[i].has_point(point)) {
- nrect = i;
- break;
- }
- }
-
- if (nrect != dock_select_rect_over_idx) {
- dock_select->queue_redraw();
- dock_select_rect_over_idx = nrect;
- }
-
- if (nrect == -1) {
- return;
- }
-
- Ref<InputEventMouseButton> mb = me;
-
- if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed() && dock_popup_selected_idx != nrect) {
- dock_slot[nrect]->move_tab_from_tab_container(dock_slot[dock_popup_selected_idx], dock_slot[dock_popup_selected_idx]->get_current_tab(), dock_slot[nrect]->get_tab_count());
-
- if (dock_slot[dock_popup_selected_idx]->get_tab_count() == 0) {
- dock_slot[dock_popup_selected_idx]->hide();
- } else {
- dock_slot[dock_popup_selected_idx]->set_current_tab(0);
- }
-
- dock_popup_selected_idx = nrect;
- dock_slot[nrect]->show();
- dock_select->queue_redraw();
-
- _update_dock_slots_visibility(true);
-
- _edit_current();
- _save_editor_layout();
- }
- }
-}
-
-void EditorNode::_dock_popup_exit() {
- dock_select_rect_over_idx = -1;
- dock_select->queue_redraw();
-}
-
-void EditorNode::_dock_pre_popup(int p_which) {
- dock_popup_selected_idx = p_which;
-}
-
-void EditorNode::_dock_move_left() {
- if (dock_popup_selected_idx < 0 || dock_popup_selected_idx >= DOCK_SLOT_MAX) {
- return;
- }
- Control *current_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab());
- Control *prev_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab() - 1);
- if (!current_ctl || !prev_ctl) {
- return;
- }
- dock_slot[dock_popup_selected_idx]->move_child(current_ctl, prev_ctl->get_index(false));
- dock_select->queue_redraw();
- _edit_current();
- _save_editor_layout();
-}
-
-void EditorNode::_dock_move_right() {
- Control *current_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab());
- Control *next_ctl = dock_slot[dock_popup_selected_idx]->get_tab_control(dock_slot[dock_popup_selected_idx]->get_current_tab() + 1);
- if (!current_ctl || !next_ctl) {
- return;
- }
- dock_slot[dock_popup_selected_idx]->move_child(next_ctl, current_ctl->get_index(false));
- dock_select->queue_redraw();
- _edit_current();
- _save_editor_layout();
-}
-
-void EditorNode::_dock_select_draw() {
- Size2 s = dock_select->get_size();
- s.y /= 2.0;
- s.x /= 6.0;
-
- Color used = Color(0.6, 0.6, 0.6, 0.8);
- Color used_selected = Color(0.8, 0.8, 0.8, 0.8);
- Color tab_selected = theme->get_color(SNAME("mono_color"), EditorStringName(Editor));
- Color unused = used;
- unused.a = 0.4;
- Color unusable = unused;
- unusable.a = 0.1;
-
- Rect2 unr(s.x * 2, 0, s.x * 2, s.y * 2);
- unr.position += Vector2(2, 5);
- unr.size -= Vector2(4, 7);
-
- dock_select->draw_rect(unr, unusable);
-
- dock_tab_move_left->set_disabled(true);
- dock_tab_move_right->set_disabled(true);
-
- if (dock_popup_selected_idx != -1 && dock_slot[dock_popup_selected_idx]->get_tab_count()) {
- dock_tab_move_left->set_disabled(dock_slot[dock_popup_selected_idx]->get_current_tab() == 0);
- dock_tab_move_right->set_disabled(dock_slot[dock_popup_selected_idx]->get_current_tab() >= dock_slot[dock_popup_selected_idx]->get_tab_count() - 1);
- }
-
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- Vector2 ofs;
-
- switch (i) {
- case DOCK_SLOT_LEFT_UL: {
- } break;
- case DOCK_SLOT_LEFT_BL: {
- ofs.y += s.y;
- } break;
- case DOCK_SLOT_LEFT_UR: {
- ofs.x += s.x;
- } break;
- case DOCK_SLOT_LEFT_BR: {
- ofs += s;
- } break;
- case DOCK_SLOT_RIGHT_UL: {
- ofs.x += s.x * 4;
- } break;
- case DOCK_SLOT_RIGHT_BL: {
- ofs.x += s.x * 4;
- ofs.y += s.y;
-
- } break;
- case DOCK_SLOT_RIGHT_UR: {
- ofs.x += s.x * 4;
- ofs.x += s.x;
-
- } break;
- case DOCK_SLOT_RIGHT_BR: {
- ofs.x += s.x * 4;
- ofs += s;
-
- } break;
- }
-
- Rect2 r(ofs, s);
- dock_select_rect[i] = r;
- r.position += Vector2(2, 5);
- r.size -= Vector2(4, 7);
-
- if (i == dock_select_rect_over_idx) {
- dock_select->draw_rect(r, used_selected);
- } else if (dock_slot[i]->get_tab_count() == 0) {
- dock_select->draw_rect(r, unused);
- } else {
- dock_select->draw_rect(r, used);
- }
-
- for (int j = 0; j < MIN(3, dock_slot[i]->get_tab_count()); j++) {
- int xofs = (r.size.width / 3) * j;
- Color c = used;
- if (i == dock_popup_selected_idx && (dock_slot[i]->get_current_tab() > 3 || dock_slot[i]->get_current_tab() == j)) {
- c = tab_selected;
- }
- dock_select->draw_rect(Rect2(2 + ofs.x + xofs, ofs.y, r.size.width / 3 - 1, 3), c);
- }
- }
-}
-
void EditorNode::_save_editor_layout() {
if (waiting_for_first_scan) {
return; // Scanning, do not touch docks.
@@ -4981,7 +4740,7 @@ void EditorNode::_save_editor_layout() {
// Load and amend existing config if it exists.
config->load(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
- _save_docks_to_config(config, "docks");
+ editor_dock_manager->save_docks_to_config(config, "docks");
_save_open_scenes_to_config(config);
_save_central_editor_layout_to_config(config);
editor_data.get_plugin_window_layout(config);
@@ -4989,85 +4748,6 @@ void EditorNode::_save_editor_layout() {
config->save(EditorPaths::get_singleton()->get_project_settings_dir().path_join("editor_layout.cfg"));
}
-void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section) {
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- String names;
- for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) {
- String name = dock_slot[i]->get_tab_control(j)->get_name();
- if (!names.is_empty()) {
- names += ",";
- }
- names += name;
- }
-
- String config_key = "dock_" + itos(i + 1);
-
- if (p_layout->has_section_key(p_section, config_key)) {
- p_layout->erase_section_key(p_section, config_key);
- }
-
- if (!names.is_empty()) {
- p_layout->set_value(p_section, config_key, names);
- }
-
- int selected_tab_idx = dock_slot[i]->get_current_tab();
- if (selected_tab_idx >= 0) {
- p_layout->set_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx", selected_tab_idx);
- }
- }
-
- Dictionary floating_docks_dump;
-
- for (WindowWrapper *wrapper : floating_docks) {
- Control *dock = wrapper->get_wrapped_control();
-
- Dictionary dock_dump;
- dock_dump["window_rect"] = wrapper->get_window_rect();
-
- int screen = wrapper->get_window_screen();
- dock_dump["window_screen"] = wrapper->get_window_screen();
- dock_dump["window_screen_rect"] = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
-
- String name = dock->get_name();
- floating_docks_dump[name] = dock_dump;
-
- int dock_slot_id = wrapper->get_meta("dock_slot");
- String config_key = "dock_" + itos(dock_slot_id + 1);
-
- String names = p_layout->get_value(p_section, config_key, "");
- if (names.is_empty()) {
- names = name;
- } else {
- names += "," + name;
- }
- p_layout->set_value(p_section, config_key, names);
- }
-
- p_layout->set_value(p_section, "dock_floating", floating_docks_dump);
-
- for (int i = 0; i < vsplits.size(); i++) {
- if (vsplits[i]->is_visible_in_tree()) {
- p_layout->set_value(p_section, "dock_split_" + itos(i + 1), vsplits[i]->get_split_offset());
- }
- }
-
- for (int i = 0; i < hsplits.size(); i++) {
- p_layout->set_value(p_section, "dock_hsplit_" + itos(i + 1), hsplits[i]->get_split_offset());
- }
-
- // Save FileSystemDock state.
-
- p_layout->set_value(p_section, "dock_filesystem_h_split_offset", FileSystemDock::get_singleton()->get_h_split_offset());
- p_layout->set_value(p_section, "dock_filesystem_v_split_offset", FileSystemDock::get_singleton()->get_v_split_offset());
- p_layout->set_value(p_section, "dock_filesystem_display_mode", FileSystemDock::get_singleton()->get_display_mode());
- p_layout->set_value(p_section, "dock_filesystem_file_sort", FileSystemDock::get_singleton()->get_file_sort());
- p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", FileSystemDock::get_singleton()->get_file_list_display_mode());
- PackedStringArray selected_files = FileSystemDock::get_singleton()->get_selected_paths();
- p_layout->set_value(p_section, "dock_filesystem_selected_paths", selected_files);
- Vector<String> uncollapsed_paths = FileSystemDock::get_singleton()->get_uncollapsed_paths();
- p_layout->set_value(p_section, "dock_filesystem_uncollapsed_paths", uncollapsed_paths);
-}
-
void EditorNode::_save_open_scenes_to_config(Ref<ConfigFile> p_layout) {
PackedStringArray scenes;
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
@@ -5087,10 +4767,6 @@ void EditorNode::save_editor_layout_delayed() {
editor_layout_save_delay_timer->start();
}
-void EditorNode::_dock_split_dragged(int ofs) {
- editor_layout_save_delay_timer->start();
-}
-
void EditorNode::_load_editor_layout() {
Ref<ConfigFile> config;
config.instantiate();
@@ -5113,227 +4789,13 @@ void EditorNode::_load_editor_layout() {
return;
}
- _load_docks_from_config(config, "docks");
+ editor_dock_manager->load_docks_from_config(config, "docks");
_load_open_scenes_from_config(config);
_load_central_editor_layout_from_config(config);
editor_data.set_plugin_window_layout(config);
}
-void EditorNode::_update_dock_slots_visibility(bool p_keep_selected_tabs) {
- if (!docks_visible) {
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- dock_slot[i]->hide();
- }
-
- for (int i = 0; i < vsplits.size(); i++) {
- vsplits[i]->hide();
- }
-
- right_hsplit->hide();
- } else {
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- int first_tab_visible = -1;
- for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) {
- if (!dock_slot[i]->is_tab_hidden(j)) {
- first_tab_visible = j;
- break;
- }
- }
- if (first_tab_visible >= 0) {
- dock_slot[i]->show();
- if (p_keep_selected_tabs) {
- int current_tab = dock_slot[i]->get_current_tab();
- if (dock_slot[i]->is_tab_hidden(current_tab)) {
- dock_slot[i]->set_block_signals(true);
- dock_slot[i]->select_next_available();
- dock_slot[i]->set_block_signals(false);
- }
- } else {
- dock_slot[i]->set_block_signals(true);
- dock_slot[i]->set_current_tab(first_tab_visible);
- dock_slot[i]->set_block_signals(false);
- }
- } else {
- dock_slot[i]->hide();
- }
- }
-
- for (int i = 0; i < vsplits.size(); i++) {
- bool in_use = dock_slot[i * 2 + 0]->is_visible() || dock_slot[i * 2 + 1]->is_visible();
- vsplits[i]->set_visible(in_use);
- }
-
- right_hsplit->set_visible(right_l_vsplit->is_visible() || right_r_vsplit->is_visible());
- }
-}
-
-void EditorNode::_dock_tab_changed(int p_tab) {
- // Update visibility but don't set current tab.
- _update_dock_slots_visibility(true);
-}
-
-void EditorNode::_restore_floating_dock(const Dictionary &p_dock_dump, Control *p_dock, int p_slot_index) {
- WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(p_dock);
- if (!wrapper) {
- _dock_make_float(p_dock, p_slot_index, false);
- wrapper = floating_docks[floating_docks.size() - 1];
- }
-
- wrapper->restore_window_from_saved_position(
- p_dock_dump.get("window_rect", Rect2i()),
- p_dock_dump.get("window_screen", -1),
- p_dock_dump.get("window_screen_rect", Rect2i()));
-}
-
-void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
- Dictionary floating_docks_dump = p_layout->get_value(p_section, "dock_floating", Dictionary());
-
- bool restore_window_on_load = EDITOR_GET("interface/multi_window/restore_windows_on_load");
-
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1))) {
- continue;
- }
-
- Vector<String> names = String(p_layout->get_value(p_section, "dock_" + itos(i + 1))).split(",");
-
- for (int j = names.size() - 1; j >= 0; j--) {
- const String &name = names[j];
-
- // FIXME: Find it, in a horribly inefficient way.
- int atidx = -1;
- Control *node = nullptr;
- for (int k = 0; k < DOCK_SLOT_MAX; k++) {
- if (!dock_slot[k]->has_node(name)) {
- continue;
- }
- node = Object::cast_to<Control>(dock_slot[k]->get_node(name));
- if (!node) {
- continue;
- }
- atidx = k;
- break;
- }
-
- if (atidx == -1) {
- // Try floating docks.
- for (WindowWrapper *wrapper : floating_docks) {
- if (wrapper->get_meta("dock_name") == name) {
- if (restore_window_on_load && floating_docks_dump.has(name)) {
- _restore_floating_dock(floating_docks_dump[name], wrapper, i);
- } else {
- atidx = wrapper->get_meta("dock_slot");
- node = wrapper->get_wrapped_control();
- wrapper->set_window_enabled(false);
- }
- break;
- }
- }
- }
- if (!node) {
- // Well, it's not anywhere.
- continue;
- }
-
- if (atidx == i) {
- dock_slot[i]->move_child(node, 0);
- } else if (atidx != -1) {
- dock_slot[i]->move_tab_from_tab_container(dock_slot[atidx], dock_slot[atidx]->get_tab_idx_from_control(node), 0);
- }
-
- WindowWrapper *wrapper = Object::cast_to<WindowWrapper>(node);
- if (restore_window_on_load && floating_docks_dump.has(name)) {
- if (!dock_slot[i]->is_tab_hidden(dock_slot[i]->get_tab_idx_from_control(node))) {
- _restore_floating_dock(floating_docks_dump[name], node, i);
- }
- } else if (wrapper) {
- wrapper->set_window_enabled(false);
- }
- }
-
- if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx")) {
- continue;
- }
-
- int selected_tab_idx = p_layout->get_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx");
- if (selected_tab_idx >= 0 && selected_tab_idx < dock_slot[i]->get_tab_count()) {
- callable_mp(dock_slot[i], &TabContainer::set_current_tab).call_deferred(selected_tab_idx);
- }
- }
-
- 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;
- }
- int ofs = p_layout->get_value(p_section, "dock_hsplit_" + itos(i + 1));
- hsplits[i]->set_split_offset(ofs);
- }
-
- _update_dock_slots_visibility(false);
-
- // FileSystemDock.
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_h_split_offset")) {
- int fs_h_split_ofs = p_layout->get_value(p_section, "dock_filesystem_h_split_offset");
- FileSystemDock::get_singleton()->set_h_split_offset(fs_h_split_ofs);
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_v_split_offset")) {
- int fs_v_split_ofs = p_layout->get_value(p_section, "dock_filesystem_v_split_offset");
- FileSystemDock::get_singleton()->set_v_split_offset(fs_v_split_ofs);
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_display_mode")) {
- FileSystemDock::DisplayMode dock_filesystem_display_mode = FileSystemDock::DisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_display_mode")));
- FileSystemDock::get_singleton()->set_display_mode(dock_filesystem_display_mode);
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_file_sort")) {
- FileSystemDock::FileSortOption dock_filesystem_file_sort = FileSystemDock::FileSortOption(int(p_layout->get_value(p_section, "dock_filesystem_file_sort")));
- FileSystemDock::get_singleton()->set_file_sort(dock_filesystem_file_sort);
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) {
- FileSystemDock::FileListDisplayMode dock_filesystem_file_list_display_mode = FileSystemDock::FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode")));
- FileSystemDock::get_singleton()->set_file_list_display_mode(dock_filesystem_file_list_display_mode);
- }
-
- if (p_layout->has_section_key(p_section, "dock_filesystem_selected_paths")) {
- PackedStringArray dock_filesystem_selected_paths = p_layout->get_value(p_section, "dock_filesystem_selected_paths");
- for (int i = 0; i < dock_filesystem_selected_paths.size(); i++) {
- FileSystemDock::get_singleton()->select_file(dock_filesystem_selected_paths[i]);
- }
- }
-
- // Restore collapsed state of FileSystemDock.
- PackedStringArray uncollapsed_tis;
- if (p_layout->has_section_key(p_section, "dock_filesystem_uncollapsed_paths")) {
- uncollapsed_tis = p_layout->get_value(p_section, "dock_filesystem_uncollapsed_paths");
- } else {
- uncollapsed_tis = { "res://" };
- }
-
- if (!uncollapsed_tis.is_empty()) {
- for (int i = 0; i < uncollapsed_tis.size(); i++) {
- TreeItem *uncollapsed_ti = FileSystemDock::get_singleton()->get_tree_control()->get_item_with_metadata(uncollapsed_tis[i], 0);
- if (uncollapsed_ti) {
- uncollapsed_ti->set_collapsed(false);
- }
- }
- FileSystemDock::get_singleton()->get_tree_control()->queue_redraw();
- }
-}
-
void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_file) {
// Bottom panel.
@@ -5582,7 +5044,7 @@ void EditorNode::_layout_menu_option(int p_id) {
layout_dialog->popup_centered();
} break;
case SETTINGS_LAYOUT_DEFAULT: {
- _load_docks_from_config(default_layout, "docks");
+ editor_dock_manager->load_docks_from_config(default_layout, "docks");
_save_editor_layout();
} break;
default: {
@@ -5593,7 +5055,7 @@ void EditorNode::_layout_menu_option(int p_id) {
return; // No config.
}
- _load_docks_from_config(config, editor_layouts->get_item_text(p_id));
+ editor_dock_manager->load_docks_from_config(config, editor_layouts->get_item_text(p_id));
_save_editor_layout();
}
}
@@ -5803,15 +5265,6 @@ void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
}
}
-void EditorNode::set_docks_visible(bool p_show) {
- docks_visible = p_show;
- _update_dock_slots_visibility(true);
-}
-
-bool EditorNode::get_docks_visible() const {
- return docks_visible;
-}
-
void EditorNode::_toggle_distraction_free_mode() {
if (EDITOR_GET("interface/editor/separate_distraction_mode")) {
int screen = -1;
@@ -5838,11 +5291,11 @@ void EditorNode::set_distraction_free_mode(bool p_enter) {
distraction_free->set_pressed(p_enter);
if (p_enter) {
- if (docks_visible) {
- set_docks_visible(false);
+ if (editor_dock_manager->are_docks_visible()) {
+ editor_dock_manager->set_docks_visible(false);
}
} else {
- set_docks_visible(true);
+ editor_dock_manager->set_docks_visible(true);
}
}
@@ -5850,35 +5303,6 @@ bool EditorNode::is_distraction_free_mode_enabled() const {
return distraction_free->is_pressed();
}
-void EditorNode::add_control_to_dock(DockSlot p_slot, Control *p_control) {
- ERR_FAIL_INDEX(p_slot, DOCK_SLOT_MAX);
- dock_slot[p_slot]->add_child(p_control);
- _update_dock_slots_visibility();
-}
-
-void EditorNode::remove_control_from_dock(Control *p_control) {
- // If the dock is floating, close it first.
- for (WindowWrapper *wrapper : floating_docks) {
- if (p_control == wrapper->get_wrapped_control()) {
- wrapper->set_window_enabled(false);
- break;
- }
- }
-
- Control *dock = nullptr;
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- if (p_control->get_parent() == dock_slot[i]) {
- dock = dock_slot[i];
- break;
- }
- }
-
- ERR_FAIL_NULL_MSG(dock, "Control was not in dock.");
-
- dock->remove_child(p_control);
- _update_dock_slots_visibility();
-}
-
Variant EditorNode::drag_resource(const Ref<Resource> &p_res, Control *p_from) {
Control *drag_control = memnew(Control);
TextureRect *drag_preview = memnew(TextureRect);
@@ -6632,9 +6056,7 @@ void EditorNode::_resource_loaded(Ref<Resource> p_resource, const String &p_path
void EditorNode::_feature_profile_changed() {
Ref<EditorFeatureProfile> profile = feature_profile_manager->get_current_profile();
// FIXME: Close all floating docks to avoid crash.
- for (WindowWrapper *wrapper : floating_docks) {
- wrapper->set_window_enabled(false);
- }
+ editor_dock_manager->close_all_floating_docks();
TabContainer *import_tabs = cast_to<TabContainer>(ImportDock::get_singleton()->get_parent());
TabContainer *node_tabs = cast_to<TabContainer>(NodeDock::get_singleton()->get_parent());
TabContainer *fs_tabs = cast_to<TabContainer>(FileSystemDock::get_singleton()->get_parent());
@@ -6669,7 +6091,7 @@ void EditorNode::_feature_profile_changed() {
}
}
- _update_dock_slots_visibility();
+ editor_dock_manager->update_dock_slots_visibility();
}
void EditorNode::_bind_methods() {
@@ -7079,127 +6501,96 @@ EditorNode::EditorNode() {
title_bar = memnew(EditorTitleBar);
main_vbox->add_child(title_bar);
- left_l_hsplit = memnew(HSplitContainer);
+ 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);
- left_l_vsplit = memnew(VSplitContainer);
+ 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);
- dock_slot[DOCK_SLOT_LEFT_UL] = memnew(TabContainer);
- left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UL]);
- dock_slot[DOCK_SLOT_LEFT_BL] = memnew(TabContainer);
- left_l_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BL]);
- left_r_hsplit = memnew(HSplitContainer);
+ TabContainer *dock_slot[EditorDockManager::DOCK_SLOT_MAX];
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL]->set_name("DockSlotLeftUL");
+ left_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL]);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL]->set_name("DockSlotLeftBL");
+ left_l_vsplit->add_child(dock_slot[EditorDockManager::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(VSplitContainer);
+ 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);
- dock_slot[DOCK_SLOT_LEFT_UR] = memnew(TabContainer);
- left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_UR]);
- dock_slot[DOCK_SLOT_LEFT_BR] = memnew(TabContainer);
- left_r_vsplit->add_child(dock_slot[DOCK_SLOT_LEFT_BR]);
-
- main_hsplit = memnew(HSplitContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR]->set_name("DockSlotLeftUR");
+ left_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR]);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR]->set_name("DockSlotLeftBR");
+ left_r_vsplit->add_child(dock_slot[EditorDockManager::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);
- center_split = memnew(VSplitContainer);
+ center_split = memnew(DockSplitContainer);
+ center_split->set_name("DockVSplitCenter");
+ center_split->set_vertical(true);
center_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
center_split->set_collapsed(false);
center_vb->add_child(center_split);
- right_hsplit = memnew(HSplitContainer);
+ right_hsplit = memnew(DockSplitContainer);
+ right_hsplit->set_name("DockHSplitRight");
main_hsplit->add_child(right_hsplit);
- right_l_vsplit = memnew(VSplitContainer);
+ right_l_vsplit = memnew(DockSplitContainer);
+ right_l_vsplit->set_name("DockVSplitRightL");
+ right_l_vsplit->set_vertical(true);
right_hsplit->add_child(right_l_vsplit);
- dock_slot[DOCK_SLOT_RIGHT_UL] = memnew(TabContainer);
- right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UL]);
- dock_slot[DOCK_SLOT_RIGHT_BL] = memnew(TabContainer);
- right_l_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BL]);
-
- right_r_vsplit = memnew(VSplitContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL]->set_name("DockSlotRightUL");
+ right_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL]);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL]->set_name("DockSlotRightBL");
+ right_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL]);
+
+ right_r_vsplit = memnew(DockSplitContainer);
+ right_r_vsplit->set_name("DockVSplitRightR");
+ right_r_vsplit->set_vertical(true);
right_hsplit->add_child(right_r_vsplit);
- dock_slot[DOCK_SLOT_RIGHT_UR] = memnew(TabContainer);
- right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_UR]);
- dock_slot[DOCK_SLOT_RIGHT_BR] = memnew(TabContainer);
- right_r_vsplit->add_child(dock_slot[DOCK_SLOT_RIGHT_BR]);
-
- // Store them for easier access.
- vsplits.push_back(left_l_vsplit);
- vsplits.push_back(left_r_vsplit);
- vsplits.push_back(right_l_vsplit);
- vsplits.push_back(right_r_vsplit);
-
- hsplits.push_back(left_l_hsplit);
- hsplits.push_back(left_r_hsplit);
- hsplits.push_back(main_hsplit);
- hsplits.push_back(right_hsplit);
-
- for (int i = 0; i < vsplits.size(); i++) {
- vsplits[i]->connect("dragged", callable_mp(this, &EditorNode::_dock_split_dragged));
- hsplits[i]->connect("dragged", callable_mp(this, &EditorNode::_dock_split_dragged));
- }
-
- dock_select_popup = memnew(PopupPanel);
- gui_base->add_child(dock_select_popup);
- VBoxContainer *dock_vb = memnew(VBoxContainer);
- dock_select_popup->add_child(dock_vb);
-
- HBoxContainer *dock_hb = memnew(HBoxContainer);
- dock_tab_move_left = memnew(Button);
- dock_tab_move_left->set_flat(true);
- dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE);
- dock_tab_move_left->connect("pressed", callable_mp(this, &EditorNode::_dock_move_left));
- dock_hb->add_child(dock_tab_move_left);
-
- Label *dock_label = memnew(Label);
- dock_label->set_text(TTR("Dock Position"));
- dock_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- dock_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- dock_hb->add_child(dock_label);
-
- dock_tab_move_right = memnew(Button);
- dock_tab_move_right->set_flat(true);
- dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE);
- dock_tab_move_right->connect("pressed", callable_mp(this, &EditorNode::_dock_move_right));
-
- dock_hb->add_child(dock_tab_move_right);
- dock_vb->add_child(dock_hb);
-
- dock_select = memnew(Control);
- dock_select->set_custom_minimum_size(Size2(128, 64) * EDSCALE);
- dock_select->connect("gui_input", callable_mp(this, &EditorNode::_dock_select_input));
- dock_select->connect("draw", callable_mp(this, &EditorNode::_dock_select_draw));
- dock_select->connect("mouse_exited", callable_mp(this, &EditorNode::_dock_popup_exit));
- dock_select->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- dock_vb->add_child(dock_select);
-
- if (!SceneTree::get_singleton()->get_root()->is_embedding_subwindows() && !EDITOR_GET("interface/editor/single_window_mode") && EDITOR_GET("interface/multi_window/enable")) {
- dock_float = memnew(Button);
- dock_float->set_icon(theme->get_icon("MakeFloating", EditorStringName(EditorIcons)));
- dock_float->set_text(TTR("Make Floating"));
- dock_float->set_focus_mode(Control::FOCUS_NONE);
- dock_float->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
- dock_float->connect("pressed", callable_mp(this, &EditorNode::_dock_make_selected_float));
-
- dock_vb->add_child(dock_float);
- }
-
- dock_select_popup->reset_size();
-
- for (int i = 0; i < DOCK_SLOT_MAX; i++) {
- dock_slot[i]->set_custom_minimum_size(Size2(170, 0) * EDSCALE);
- dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- dock_slot[i]->set_popup(dock_select_popup);
- dock_slot[i]->connect("pre_popup_pressed", callable_mp(this, &EditorNode::_dock_pre_popup).bind(i));
- dock_slot[i]->set_drag_to_rearrange_enabled(true);
- dock_slot[i]->set_tabs_rearrange_group(1);
- dock_slot[i]->connect("tab_changed", callable_mp(this, &EditorNode::_dock_tab_changed));
- dock_slot[i]->set_use_hidden_tabs_for_min_size(true);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR]->set_name("DockSlotRightUR");
+ right_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR]);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR] = memnew(TabContainer);
+ dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR]->set_name("DockSlotRightBR");
+ right_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR]);
+
+ editor_dock_manager = memnew(EditorDockManager);
+ editor_dock_manager->connect("layout_changed", callable_mp(this, &EditorNode::_save_editor_layout));
+
+ // Save the splits for easier access.
+ editor_dock_manager->add_vsplit(left_l_vsplit);
+ editor_dock_manager->add_vsplit(left_r_vsplit);
+ 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);
+
+ for (int i = 0; i < EditorDockManager::DOCK_SLOT_MAX; i++) {
+ editor_dock_manager->register_dock_slot((EditorDockManager::DockSlot)i, dock_slot[i]);
}
editor_layout_save_delay_timer = memnew(Timer);
@@ -7642,37 +7033,22 @@ EditorNode::EditorNode() {
history_dock = memnew(HistoryDock);
// Scene: Top left.
- dock_slot[DOCK_SLOT_LEFT_UR]->add_child(SceneTreeDock::get_singleton());
- dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(dock_slot[DOCK_SLOT_LEFT_UR]->get_tab_idx_from_control(SceneTreeDock::get_singleton()), TTR("Scene"));
+ editor_dock_manager->add_control_to_dock(EditorDockManager::DOCK_SLOT_LEFT_UR, SceneTreeDock::get_singleton(), TTR("Scene"));
// Import: Top left, behind Scene.
- dock_slot[DOCK_SLOT_LEFT_UR]->add_child(ImportDock::get_singleton());
- dock_slot[DOCK_SLOT_LEFT_UR]->set_tab_title(dock_slot[DOCK_SLOT_LEFT_UR]->get_tab_idx_from_control(ImportDock::get_singleton()), TTR("Import"));
+ editor_dock_manager->add_control_to_dock(EditorDockManager::DOCK_SLOT_LEFT_UR, ImportDock::get_singleton(), TTR("Import"));
// FileSystem: Bottom left.
- dock_slot[DOCK_SLOT_LEFT_BR]->add_child(FileSystemDock::get_singleton());
- dock_slot[DOCK_SLOT_LEFT_BR]->set_tab_title(dock_slot[DOCK_SLOT_LEFT_BR]->get_tab_idx_from_control(FileSystemDock::get_singleton()), TTR("FileSystem"));
+ editor_dock_manager->add_control_to_dock(EditorDockManager::DOCK_SLOT_LEFT_BR, FileSystemDock::get_singleton(), TTR("FileSystem"));
// Inspector: Full height right.
- dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(InspectorDock::get_singleton());
- dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(dock_slot[DOCK_SLOT_RIGHT_UL]->get_tab_idx_from_control(InspectorDock::get_singleton()), TTR("Inspector"));
+ editor_dock_manager->add_control_to_dock(EditorDockManager::DOCK_SLOT_RIGHT_UL, InspectorDock::get_singleton(), TTR("Inspector"));
// Node: Full height right, behind Inspector.
- dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(NodeDock::get_singleton());
- dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(dock_slot[DOCK_SLOT_RIGHT_UL]->get_tab_idx_from_control(NodeDock::get_singleton()), TTR("Node"));
+ editor_dock_manager->add_control_to_dock(EditorDockManager::DOCK_SLOT_RIGHT_UL, NodeDock::get_singleton(), TTR("Node"));
// History: Full height right, behind Node.
- dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(history_dock);
- dock_slot[DOCK_SLOT_RIGHT_UL]->set_tab_title(dock_slot[DOCK_SLOT_RIGHT_UL]->get_tab_idx_from_control(history_dock), TTR("History"));
-
- // Hide unused dock slots and vsplits.
- dock_slot[DOCK_SLOT_LEFT_UL]->hide();
- dock_slot[DOCK_SLOT_LEFT_BL]->hide();
- dock_slot[DOCK_SLOT_RIGHT_BL]->hide();
- dock_slot[DOCK_SLOT_RIGHT_UR]->hide();
- dock_slot[DOCK_SLOT_RIGHT_BR]->hide();
- left_l_vsplit->hide();
- right_r_vsplit->hide();
+ editor_dock_manager->add_control_to_dock(EditorDockManager::DOCK_SLOT_RIGHT_UL, history_dock, TTR("History"));
// 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(270 * EDSCALE);
@@ -7687,7 +7063,8 @@ EditorNode::EditorNode() {
default_layout->set_value(docks_section, "dock_4", "FileSystem");
default_layout->set_value(docks_section, "dock_5", "Inspector,Node,History");
- for (int i = 0; i < vsplits.size(); i++) {
+ // There are 4 vsplits and 4 hsplits.
+ for (int i = 0; i < editor_dock_manager->get_vsplit_count(); i++) {
default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0);
}
default_layout->set_value(docks_section, "dock_hsplit_1", 0);
@@ -8120,6 +7497,7 @@ EditorNode::~EditorNode() {
memdelete(editor_plugins_force_input_forwarding);
memdelete(progress_hb);
memdelete(surface_upgrade_tool);
+ memdelete(editor_dock_manager);
EditorSettings::destroy();
EditorColorMap::finish();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index c72a8f9324..f1dea0c11e 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -73,10 +73,12 @@ class AudioStreamPreviewGenerator;
class BackgroundProgress;
class DependencyEditor;
class DependencyErrorDialog;
+class DockSplitContainer;
class DynamicFontImportSettingsDialog;
class EditorAbout;
class EditorBuildProfileManager;
class EditorCommandPalette;
+class EditorDockManager;
class EditorExport;
class EditorExtensionManager;
class EditorFeatureProfileManager;
@@ -121,18 +123,6 @@ class EditorNode : public Node {
GDCLASS(EditorNode, Node);
public:
- enum DockSlot {
- DOCK_SLOT_LEFT_UL,
- DOCK_SLOT_LEFT_BL,
- DOCK_SLOT_LEFT_UR,
- DOCK_SLOT_LEFT_BR,
- DOCK_SLOT_RIGHT_UL,
- DOCK_SLOT_RIGHT_BL,
- DOCK_SLOT_RIGHT_UR,
- DOCK_SLOT_RIGHT_BR,
- DOCK_SLOT_MAX
- };
-
enum EditorTable {
EDITOR_2D = 0,
EDITOR_3D,
@@ -310,18 +300,15 @@ private:
String renderer_request;
// Split containers.
- HSplitContainer *left_l_hsplit = nullptr;
- VSplitContainer *left_l_vsplit = nullptr;
- HSplitContainer *left_r_hsplit = nullptr;
- VSplitContainer *left_r_vsplit = nullptr;
- HSplitContainer *main_hsplit = nullptr;
- HSplitContainer *right_hsplit = nullptr;
- VSplitContainer *right_l_vsplit = nullptr;
- VSplitContainer *right_r_vsplit = nullptr;
- VSplitContainer *center_split = nullptr;
- // To access those easily by index.
- Vector<VSplitContainer *> vsplits;
- Vector<HSplitContainer *> hsplits;
+ 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;
// Main tabs.
EditorSceneTabs *scene_tabs = nullptr;
@@ -426,20 +413,8 @@ private:
Button *new_inherited_button = nullptr;
String open_import_request;
- Vector<WindowWrapper *> floating_docks;
-
- Button *dock_float = nullptr;
- Button *dock_tab_move_left = nullptr;
- Button *dock_tab_move_right = nullptr;
- Control *dock_select = nullptr;
- PopupPanel *dock_select_popup = nullptr;
- Rect2 dock_select_rect[DOCK_SLOT_MAX];
- TabContainer *dock_slot[DOCK_SLOT_MAX];
+ EditorDockManager *editor_dock_manager = nullptr;
Timer *editor_layout_save_delay_timer = nullptr;
- bool docks_visible = true;
- int dock_popup_selected_idx = -1;
- int dock_select_rect_over_idx = -1;
-
Button *distraction_free = nullptr;
Vector<BottomPanelItem> bottom_panel_items;
@@ -634,17 +609,6 @@ private:
bool _find_scene_in_use(Node *p_node, const String &p_path) const;
- void _dock_select_input(const Ref<InputEvent> &p_input);
- void _dock_move_left();
- void _dock_move_right();
- void _dock_select_draw();
- void _dock_pre_popup(int p_which);
- void _dock_split_dragged(int ofs);
- void _dock_popup_exit();
- void _dock_floating_close_request(WindowWrapper *p_wrapper);
- void _dock_make_selected_float();
- void _dock_make_float(Control *p_control, int p_slot_index, bool p_show_window = true);
-
void _proceed_closing_scene_tabs();
bool _is_closing_editor() const;
@@ -655,11 +619,6 @@ private:
void _save_editor_layout();
void _load_editor_layout();
- void _save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section);
- void _restore_floating_dock(const Dictionary &p_dock_dump, Control *p_wrapper, int p_slot_index);
- void _load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section);
- void _update_dock_slots_visibility(bool p_keep_selected_tabs = false);
- void _dock_tab_changed(int p_tab);
void _save_central_editor_layout_to_config(Ref<ConfigFile> p_config_file);
void _load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file);
@@ -772,15 +731,9 @@ public:
void new_inherited_scene() { _menu_option_confirm(FILE_NEW_INHERITED_SCENE, false); }
- void set_docks_visible(bool p_show);
- bool get_docks_visible() const;
-
void set_distraction_free_mode(bool p_enter);
bool is_distraction_free_mode_enabled() const;
- void add_control_to_dock(DockSlot p_slot, Control *p_control);
- void remove_control_from_dock(Control *p_control);
-
void set_addon_plugin_enabled(const String &p_addon, bool p_enabled, bool p_config_changed = false);
bool is_addon_plugin_enabled(const String &p_addon) const;
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 49c62a3a6c..f0044edff2 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "editor_plugin.h"
#include "editor/debugger/editor_debugger_node.h"
+#include "editor/editor_dock_manager.h"
#include "editor/editor_file_system.h"
#include "editor/editor_inspector.h"
#include "editor/editor_interface.h"
@@ -84,12 +85,12 @@ Button *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const Stri
void EditorPlugin::add_control_to_dock(DockSlot p_slot, Control *p_control) {
ERR_FAIL_NULL(p_control);
- EditorNode::get_singleton()->add_control_to_dock(EditorNode::DockSlot(p_slot), p_control);
+ EditorDockManager::get_singleton()->add_control_to_dock(EditorDockManager::DockSlot(p_slot), p_control);
}
void EditorPlugin::remove_control_from_docks(Control *p_control) {
ERR_FAIL_NULL(p_control);
- EditorNode::get_singleton()->remove_control_from_dock(p_control);
+ EditorDockManager::get_singleton()->remove_control_from_dock(p_control);
}
void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 7f9d80961b..9951e604cc 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -407,8 +407,8 @@ void EditorPropertyArray::update_property() {
new_prop->connect(SNAME("object_id_selected"), callable_mp(this, &EditorPropertyArray::_object_id_selected));
new_prop->set_h_size_flags(SIZE_EXPAND_FILL);
new_prop->set_read_only(is_read_only());
- callable_mp((Node *)slot.prop, &Node::add_sibling).call_deferred(new_prop, false);
- callable_mp((Node *)slot.prop, &Node::queue_free).call_deferred();
+ slot.prop->add_sibling(new_prop, false);
+ slot.prop->queue_free();
slot.prop = new_prop;
slot.set_index(idx);
}
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 6f8b46cce0..45c74f757f 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -3665,6 +3665,70 @@ void FileSystemDock::_bind_methods() {
ADD_SIGNAL(MethodInfo("display_mode_changed"));
}
+void FileSystemDock::save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
+ p_layout->set_value(p_section, "dock_filesystem_h_split_offset", get_h_split_offset());
+ p_layout->set_value(p_section, "dock_filesystem_v_split_offset", get_v_split_offset());
+ p_layout->set_value(p_section, "dock_filesystem_display_mode", get_display_mode());
+ p_layout->set_value(p_section, "dock_filesystem_file_sort", get_file_sort());
+ p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", get_file_list_display_mode());
+ PackedStringArray selected_files = get_selected_paths();
+ p_layout->set_value(p_section, "dock_filesystem_selected_paths", selected_files);
+ Vector<String> uncollapsed_paths = get_uncollapsed_paths();
+ p_layout->set_value(p_section, "dock_filesystem_uncollapsed_paths", uncollapsed_paths);
+}
+
+void FileSystemDock::load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
+ if (p_layout->has_section_key(p_section, "dock_filesystem_h_split_offset")) {
+ int fs_h_split_ofs = p_layout->get_value(p_section, "dock_filesystem_h_split_offset");
+ set_h_split_offset(fs_h_split_ofs);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_v_split_offset")) {
+ int fs_v_split_ofs = p_layout->get_value(p_section, "dock_filesystem_v_split_offset");
+ set_v_split_offset(fs_v_split_ofs);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_display_mode")) {
+ DisplayMode dock_filesystem_display_mode = DisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_display_mode")));
+ set_display_mode(dock_filesystem_display_mode);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_file_sort")) {
+ FileSortOption dock_filesystem_file_sort = FileSortOption(int(p_layout->get_value(p_section, "dock_filesystem_file_sort")));
+ set_file_sort(dock_filesystem_file_sort);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) {
+ FileListDisplayMode dock_filesystem_file_list_display_mode = FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode")));
+ set_file_list_display_mode(dock_filesystem_file_list_display_mode);
+ }
+
+ if (p_layout->has_section_key(p_section, "dock_filesystem_selected_paths")) {
+ PackedStringArray dock_filesystem_selected_paths = p_layout->get_value(p_section, "dock_filesystem_selected_paths");
+ for (int i = 0; i < dock_filesystem_selected_paths.size(); i++) {
+ select_file(dock_filesystem_selected_paths[i]);
+ }
+ }
+
+ // Restore collapsed state.
+ PackedStringArray uncollapsed_tis;
+ if (p_layout->has_section_key(p_section, "dock_filesystem_uncollapsed_paths")) {
+ uncollapsed_tis = p_layout->get_value(p_section, "dock_filesystem_uncollapsed_paths");
+ } else {
+ uncollapsed_tis = { "res://" };
+ }
+
+ if (!uncollapsed_tis.is_empty()) {
+ for (int i = 0; i < uncollapsed_tis.size(); i++) {
+ TreeItem *uncollapsed_ti = get_tree_control()->get_item_with_metadata(uncollapsed_tis[i], 0);
+ if (uncollapsed_ti) {
+ uncollapsed_ti->set_collapsed(false);
+ }
+ }
+ get_tree_control()->queue_redraw();
+ }
+}
+
FileSystemDock::FileSystemDock() {
singleton = this;
set_name("FileSystem");
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 5fe1389e2a..6c69acb953 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -394,13 +394,13 @@ public:
void select_file(const String &p_file);
void set_display_mode(DisplayMode p_display_mode);
- DisplayMode get_display_mode() { return display_mode; }
+ DisplayMode get_display_mode() const { return display_mode; }
void set_file_sort(FileSortOption p_file_sort);
- FileSortOption get_file_sort() { return file_sort; }
+ FileSortOption get_file_sort() const { return file_sort; }
void set_file_list_display_mode(FileListDisplayMode p_mode);
- FileListDisplayMode get_file_list_display_mode() { return file_list_display_mode; };
+ FileListDisplayMode get_file_list_display_mode() const { return file_list_display_mode; };
Tree *get_tree_control() { return tree; }
@@ -408,6 +408,9 @@ public:
void remove_resource_tooltip_plugin(const Ref<EditorResourceTooltipPlugin> &p_plugin);
Control *create_tooltip_for_path(const String &p_path) const;
+ void save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const;
+ void load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section);
+
FileSystemDock();
~FileSystemDock();
};
diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index 2fa54ac1dc..d43c09fb59 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
#include "core/os/time.h"
+#include "editor/editor_dock_manager.h"
#include "editor/editor_file_system.h"
#include "editor/editor_interface.h"
#include "editor/editor_node.h"
@@ -909,7 +910,7 @@ void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() {
}
void VersionControlEditorPlugin::register_editor() {
- EditorNode::get_singleton()->add_control_to_dock(EditorNode::DOCK_SLOT_RIGHT_UL, version_commit_dock);
+ EditorDockManager::get_singleton()->add_control_to_dock(EditorDockManager::DOCK_SLOT_RIGHT_UL, version_commit_dock);
version_control_dock_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Version Control"), version_control_dock);
@@ -929,7 +930,7 @@ void VersionControlEditorPlugin::shut_down() {
memdelete(EditorVCSInterface::get_singleton());
EditorVCSInterface::set_singleton(nullptr);
- EditorNode::get_singleton()->remove_control_from_dock(version_commit_dock);
+ EditorDockManager::get_singleton()->remove_control_from_dock(version_commit_dock);
EditorNode::get_singleton()->remove_bottom_panel_item(version_control_dock);
_set_vcs_ui_state(false);
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index 4efa4d329e..a46a1c93b5 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -46,7 +46,9 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
int frames_mixed_this_step = p_frames;
int beat_length_frames = -1;
- bool beat_loop = mp3_stream->has_loop() && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;
+ bool use_loop = looping_override ? looping : mp3_stream->loop;
+
+ bool beat_loop = use_loop && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0;
if (beat_loop) {
beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm();
}
@@ -82,7 +84,7 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
else {
//EOF
- if (mp3_stream->loop) {
+ if (use_loop) {
seek(mp3_stream->loop_offset);
loops++;
} else {
@@ -143,6 +145,25 @@ void AudioStreamPlaybackMP3::tag_used_streams() {
mp3_stream->tag_used(get_playback_position());
}
+void AudioStreamPlaybackMP3::set_parameter(const StringName &p_name, const Variant &p_value) {
+ if (p_name == SNAME("looping")) {
+ if (p_value == Variant()) {
+ looping_override = false;
+ looping = false;
+ } else {
+ looping_override = true;
+ looping = p_value;
+ }
+ }
+}
+
+Variant AudioStreamPlaybackMP3::get_parameter(const StringName &p_name) const {
+ if (looping_override && p_name == SNAME("looping")) {
+ return looping;
+ }
+ return Variant();
+}
+
AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
if (mp3d) {
mp3dec_ex_close(mp3d);
@@ -232,6 +253,10 @@ bool AudioStreamMP3::is_monophonic() const {
return false;
}
+void AudioStreamMP3::get_parameter_list(List<Parameter> *r_parameters) {
+ r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));
+}
+
void AudioStreamMP3::set_bpm(double p_bpm) {
ERR_FAIL_COND(p_bpm < 0);
bpm = p_bpm;
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index 30760703e3..7d85e0a321 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -47,6 +47,8 @@ class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
AudioFrame loop_fade[FADE_SIZE];
int loop_fade_remaining = FADE_SIZE;
+ bool looping_override = false;
+ bool looping = false;
mp3dec_ex_t *mp3d = nullptr;
uint32_t frames_mixed = 0;
bool active = false;
@@ -72,6 +74,9 @@ public:
virtual void tag_used_streams() override;
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
+ virtual Variant get_parameter(const StringName &p_name) const override;
+
AudioStreamPlaybackMP3() {}
~AudioStreamPlaybackMP3();
};
@@ -126,6 +131,8 @@ public:
virtual bool is_monophonic() const override;
+ virtual void get_parameter_list(List<Parameter> *r_parameters) override;
+
AudioStreamMP3();
virtual ~AudioStreamMP3();
};
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index 7ec0b697bf..e6003f35df 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -46,8 +46,9 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
int todo = p_frames;
int beat_length_frames = -1;
- bool beat_loop = vorbis_stream->has_loop();
- if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
+ bool use_loop = looping_override ? looping : vorbis_stream->loop;
+
+ if (use_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) {
beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm();
}
@@ -99,7 +100,7 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
} else
**/
- if (beat_loop && beat_length_frames <= (int)frames_mixed) {
+ if (use_loop && beat_length_frames <= (int)frames_mixed) {
// End of file when doing beat-based looping. <= used instead of == because importer editing
if (!have_packets_left && !have_samples_left) {
//Nothing remaining, so do nothing.
@@ -125,7 +126,7 @@ int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
if (!have_packets_left && !have_samples_left) {
// Actual end of file!
bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
- if (vorbis_stream->loop && is_not_empty) {
+ if (use_loop && is_not_empty) {
//loop
seek(vorbis_stream->loop_offset);
@@ -257,6 +258,25 @@ void AudioStreamPlaybackOggVorbis::tag_used_streams() {
vorbis_stream->tag_used(get_playback_position());
}
+void AudioStreamPlaybackOggVorbis::set_parameter(const StringName &p_name, const Variant &p_value) {
+ if (p_name == SNAME("looping")) {
+ if (p_value == Variant()) {
+ looping_override = false;
+ looping = false;
+ } else {
+ looping_override = true;
+ looping = p_value;
+ }
+ }
+}
+
+Variant AudioStreamPlaybackOggVorbis::get_parameter(const StringName &p_name) const {
+ if (looping_override && p_name == SNAME("looping")) {
+ return looping;
+ }
+ return Variant();
+}
+
void AudioStreamPlaybackOggVorbis::seek(double p_time) {
ERR_FAIL_COND(!ready);
ERR_FAIL_COND(vorbis_stream.is_null());
@@ -493,6 +513,10 @@ bool AudioStreamOggVorbis::is_monophonic() const {
return false;
}
+void AudioStreamOggVorbis::get_parameter_list(List<Parameter> *r_parameters) {
+ r_parameters->push_back(Parameter(PropertyInfo(Variant::BOOL, "looping", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_CHECKABLE), Variant()));
+}
+
void AudioStreamOggVorbis::_bind_methods() {
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer);
ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file);
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index 6abaeaaebd..64a7815b57 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -44,6 +44,8 @@ class AudioStreamPlaybackOggVorbis : public AudioStreamPlaybackResampled {
uint32_t frames_mixed = 0;
bool active = false;
+ bool looping_override = false;
+ bool looping = false;
int loops = 0;
enum {
@@ -95,6 +97,9 @@ public:
virtual void tag_used_streams() override;
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
+ virtual Variant get_parameter(const StringName &p_name) const override;
+
AudioStreamPlaybackOggVorbis() {}
~AudioStreamPlaybackOggVorbis();
};
@@ -152,6 +157,8 @@ public:
virtual bool is_monophonic() const override;
+ virtual void get_parameter_list(List<Parameter> *r_parameters) override;
+
AudioStreamOggVorbis();
virtual ~AudioStreamOggVorbis();
};
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 762e5a8323..2cc1f41b3e 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1605,7 +1605,11 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
print_verbose("Loading regular icon from " + path);
if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) {
print_verbose("- falling back to project icon: " + project_icon_path);
- ImageLoader::load_image(project_icon_path, icon);
+ if (!project_icon_path.is_empty()) {
+ ImageLoader::load_image(project_icon_path, icon);
+ } else {
+ ERR_PRINT("No project icon specified. Please specify one in the Project Settings under Application -> Config -> Icon");
+ }
}
// Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default).
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index afc5748ac5..a99964c0e0 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -36,6 +36,8 @@
#include "scene/main/window.h"
#include "scene/resources/world_2d.h"
+#define PARAM_PREFIX "parameters/"
+
void AudioStreamPlayer2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -222,9 +224,36 @@ void AudioStreamPlayer2D::_update_panning() {
last_mix_count = AudioServer::get_singleton()->get_mix_count();
}
+void AudioStreamPlayer2D::_update_stream_parameters() {
+ if (stream.is_null()) {
+ return;
+ }
+
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ const PropertyInfo &pi = K.property;
+ StringName key = PARAM_PREFIX + pi.name;
+ if (!playback_parameters.has(key)) {
+ ParameterData pd;
+ pd.path = pi.name;
+ pd.value = K.default_value;
+ playback_parameters.insert(key, pd);
+ }
+ }
+}
+
void AudioStreamPlayer2D::set_stream(Ref<AudioStream> p_stream) {
+ if (stream.is_valid()) {
+ stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer2D::_update_stream_parameters));
+ }
stop();
stream = p_stream;
+ _update_stream_parameters();
+ if (stream.is_valid()) {
+ stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer2D::_update_stream_parameters));
+ }
+ notify_property_list_changed();
}
Ref<AudioStream> AudioStreamPlayer2D::get_stream() const {
@@ -262,6 +291,10 @@ void AudioStreamPlayer2D::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+ stream_playback->set_parameter(K.value.path, K.value.value);
+ }
+
stream_playbacks.push_back(stream_playback);
setplayback = stream_playback;
setplay.set(p_from_pos);
@@ -430,6 +463,42 @@ void AudioStreamPlayer2D::_on_bus_renamed(int p_bus_index, const StringName &p_o
notify_property_list_changed();
}
+bool AudioStreamPlayer2D::_set(const StringName &p_name, const Variant &p_value) {
+ HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+ ParameterData &pd = I->value;
+ pd.value = p_value;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ playback->set_parameter(pd.path, pd.value);
+ }
+ return true;
+}
+
+bool AudioStreamPlayer2D::_get(const StringName &p_name, Variant &r_ret) const {
+ HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+
+ r_ret = I->value.value;
+ return true;
+}
+
+void AudioStreamPlayer2D::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ PropertyInfo pi = K.property;
+ pi.name = PARAM_PREFIX + pi.name;
+ p_list->push_back(pi);
+ }
+}
+
void AudioStreamPlayer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer2D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer2D::get_stream);
diff --git a/scene/2d/audio_stream_player_2d.h b/scene/2d/audio_stream_player_2d.h
index 0766f2f77d..267d6a625b 100644
--- a/scene/2d/audio_stream_player_2d.h
+++ b/scene/2d/audio_stream_player_2d.h
@@ -89,11 +89,23 @@ private:
float panning_strength = 1.0f;
float cached_global_panning_strength = 0.5f;
+ struct ParameterData {
+ StringName path;
+ Variant value;
+ };
+
+ HashMap<StringName, ParameterData> playback_parameters;
+ void _update_stream_parameters();
+
protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
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;
+
public:
void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const;
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 3971e615a1..bfdbd14cc9 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -37,6 +37,8 @@
#include "scene/main/viewport.h"
#include "scene/scene_string_names.h"
+#define PARAM_PREFIX "parameters/"
+
// Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004)
// Speaker-Placement Correction Amplitude Panning (SPCAP)
class Spcap {
@@ -525,9 +527,35 @@ Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() {
return output_volume_vector;
}
+void AudioStreamPlayer3D::_update_stream_parameters() {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ const PropertyInfo &pi = K.property;
+ StringName key = PARAM_PREFIX + pi.name;
+ if (!playback_parameters.has(key)) {
+ ParameterData pd;
+ pd.path = pi.name;
+ pd.value = K.default_value;
+ playback_parameters.insert(key, pd);
+ }
+ }
+}
+
void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) {
+ if (stream.is_valid()) {
+ stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer3D::_update_stream_parameters));
+ }
stop();
stream = p_stream;
+ _update_stream_parameters();
+ if (stream.is_valid()) {
+ stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer3D::_update_stream_parameters));
+ }
+ notify_property_list_changed();
}
Ref<AudioStream> AudioStreamPlayer3D::get_stream() const {
@@ -579,6 +607,10 @@ void AudioStreamPlayer3D::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+ stream_playback->set_parameter(K.value.path, K.value.value);
+ }
+
stream_playbacks.push_back(stream_playback);
setplayback = stream_playback;
setplay.set(p_from_pos);
@@ -818,6 +850,42 @@ void AudioStreamPlayer3D::_on_bus_renamed(int p_bus_index, const StringName &p_o
notify_property_list_changed();
}
+bool AudioStreamPlayer3D::_set(const StringName &p_name, const Variant &p_value) {
+ HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+ ParameterData &pd = I->value;
+ pd.value = p_value;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ playback->set_parameter(pd.path, pd.value);
+ }
+ return true;
+}
+
+bool AudioStreamPlayer3D::_get(const StringName &p_name, Variant &r_ret) const {
+ HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+
+ r_ret = I->value.value;
+ return true;
+}
+
+void AudioStreamPlayer3D::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ PropertyInfo pi = K.property;
+ pi.name = PARAM_PREFIX + pi.name;
+ p_list->push_back(pi);
+ }
+}
+
void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_stream", "stream"), &AudioStreamPlayer3D::set_stream);
ClassDB::bind_method(D_METHOD("get_stream"), &AudioStreamPlayer3D::get_stream);
diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h
index f20170e63b..facded1b9c 100644
--- a/scene/3d/audio_stream_player_3d.h
+++ b/scene/3d/audio_stream_player_3d.h
@@ -121,11 +121,23 @@ private:
float panning_strength = 1.0f;
float cached_global_panning_strength = 0.5f;
+ struct ParameterData {
+ StringName path;
+ Variant value;
+ };
+
+ HashMap<StringName, ParameterData> playback_parameters;
+ void _update_stream_parameters();
+
protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
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;
+
public:
void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index f3c986f9b3..bd4731d8dd 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -34,6 +34,8 @@
#include "core/math/audio_frame.h"
#include "servers/audio_server.h"
+#define PARAM_PREFIX "parameters/"
+
void AudioStreamPlayer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -88,9 +90,71 @@ void AudioStreamPlayer::_notification(int p_what) {
}
}
+void AudioStreamPlayer::_update_stream_parameters() {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ const PropertyInfo &pi = K.property;
+ StringName key = PARAM_PREFIX + pi.name;
+ if (!playback_parameters.has(key)) {
+ ParameterData pd;
+ pd.path = pi.name;
+ pd.value = K.default_value;
+ playback_parameters.insert(key, pd);
+ }
+ }
+}
+
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
+ if (stream.is_valid()) {
+ stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer::_update_stream_parameters));
+ }
stop();
stream = p_stream;
+ _update_stream_parameters();
+ if (stream.is_valid()) {
+ stream->connect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayer::_update_stream_parameters));
+ }
+ notify_property_list_changed();
+}
+
+bool AudioStreamPlayer::_set(const StringName &p_name, const Variant &p_value) {
+ HashMap<StringName, ParameterData>::Iterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+ ParameterData &pd = I->value;
+ pd.value = p_value;
+ for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
+ playback->set_parameter(pd.path, pd.value);
+ }
+ return true;
+}
+
+bool AudioStreamPlayer::_get(const StringName &p_name, Variant &r_ret) const {
+ HashMap<StringName, ParameterData>::ConstIterator I = playback_parameters.find(p_name);
+ if (!I) {
+ return false;
+ }
+
+ r_ret = I->value.value;
+ return true;
+}
+
+void AudioStreamPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (stream.is_null()) {
+ return;
+ }
+ List<AudioStream::Parameter> parameters;
+ stream->get_parameter_list(&parameters);
+ for (const AudioStream::Parameter &K : parameters) {
+ PropertyInfo pi = K.property;
+ pi.name = PARAM_PREFIX + pi.name;
+ p_list->push_back(pi);
+ }
}
Ref<AudioStream> AudioStreamPlayer::get_stream() const {
@@ -144,6 +208,10 @@ void AudioStreamPlayer::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
+ for (const KeyValue<StringName, ParameterData> &K : playback_parameters) {
+ stream_playback->set_parameter(K.value.path, K.value.value);
+ }
+
AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale);
stream_playbacks.push_back(stream_playback);
active.set();
diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h
index 9dbdccdc69..404d6fbebf 100644
--- a/scene/audio/audio_stream_player.h
+++ b/scene/audio/audio_stream_player.h
@@ -68,11 +68,23 @@ private:
Vector<AudioFrame> _get_volume_vector();
+ struct ParameterData {
+ StringName path;
+ Variant value;
+ };
+
+ HashMap<StringName, ParameterData> playback_parameters;
+ void _update_stream_parameters();
+
protected:
void _validate_property(PropertyInfo &p_property) const;
void _notification(int p_what);
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;
+
public:
void set_stream(Ref<AudioStream> p_stream);
Ref<AudioStream> get_stream() const;
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 06b32b548f..5f4586a6d5 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -39,7 +39,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
- if (sc->collapsed || !sc->_getch(0) || !sc->_getch(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) {
+ if (sc->collapsed || !sc->get_containable_child(0) || !sc->get_containable_child(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) {
return;
}
@@ -122,7 +122,7 @@ void SplitContainerDragger::_notification(int p_what) {
}
}
-Control *SplitContainer::_getch(int p_idx) const {
+Control *SplitContainer::get_containable_child(int p_idx) const {
int idx = 0;
for (int i = 0; i < get_child_count(false); i++) {
@@ -157,8 +157,8 @@ Ref<Texture2D> SplitContainer::_get_grabber_icon() const {
}
void SplitContainer::_compute_middle_sep(bool p_clamp) {
- Control *first = _getch(0);
- Control *second = _getch(1);
+ Control *first = get_containable_child(0);
+ Control *second = get_containable_child(1);
// Determine expanded children.
bool first_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND;
@@ -199,8 +199,8 @@ void SplitContainer::_compute_middle_sep(bool p_clamp) {
}
void SplitContainer::_resort() {
- Control *first = _getch(0);
- Control *second = _getch(1);
+ Control *first = get_containable_child(0);
+ Control *second = get_containable_child(1);
// If we have only one element.
if (!first || !second) {
@@ -261,7 +261,7 @@ Size2 SplitContainer::get_minimum_size() const {
int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
for (int i = 0; i < 2; i++) {
- if (!_getch(i)) {
+ if (!get_containable_child(i)) {
break;
}
@@ -273,7 +273,7 @@ Size2 SplitContainer::get_minimum_size() const {
}
}
- Size2 ms = _getch(i)->get_combined_minimum_size();
+ Size2 ms = get_containable_child(i)->get_combined_minimum_size();
if (vertical) {
minimum.height += ms.height;
@@ -325,7 +325,7 @@ int SplitContainer::get_split_offset() const {
}
void SplitContainer::clamp_split_offset() {
- if (!_getch(0) || !_getch(1)) {
+ if (!get_containable_child(0) || !get_containable_child(1)) {
return;
}
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index f008d2678b..0f45ef166d 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -79,8 +79,6 @@ private:
Ref<Texture2D> grabber_icon_v;
} theme_cache;
- Control *_getch(int p_idx) const;
-
Ref<Texture2D> _get_grabber_icon() const;
void _compute_middle_sep(bool p_clamp);
void _resort();
@@ -88,6 +86,8 @@ private:
protected:
bool is_fixed = false;
+ Control *get_containable_child(int p_idx) const;
+
void _notification(int p_what);
void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 65d88a0eba..3e3a7d2381 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -80,6 +80,16 @@ void AudioStreamPlayback::tag_used_streams() {
GDVIRTUAL_CALL(_tag_used_streams);
}
+void AudioStreamPlayback::set_parameter(const StringName &p_name, const Variant &p_value) {
+ GDVIRTUAL_CALL(_set_parameter, p_name, p_value);
+}
+
+Variant AudioStreamPlayback::get_parameter(const StringName &p_name) const {
+ Variant ret;
+ GDVIRTUAL_CALL(_get_parameter, p_name, ret);
+ return ret;
+}
+
void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_start, "from_pos")
GDVIRTUAL_BIND(_stop)
@@ -89,6 +99,8 @@ void AudioStreamPlayback::_bind_methods() {
GDVIRTUAL_BIND(_seek, "position")
GDVIRTUAL_BIND(_mix, "buffer", "rate_scale", "frames");
GDVIRTUAL_BIND(_tag_used_streams);
+ GDVIRTUAL_BIND(_set_parameter, "name", "value");
+ GDVIRTUAL_BIND(_get_parameter, "name");
}
//////////////////////////////
@@ -249,6 +261,16 @@ float AudioStream::get_tagged_frame_offset(int p_index) const {
return tagged_offsets[p_index];
}
+void AudioStream::get_parameter_list(List<Parameter> *r_parameters) {
+ TypedArray<Dictionary> ret;
+ GDVIRTUAL_CALL(_get_parameter_list, ret);
+ for (int i = 0; i < ret.size(); i++) {
+ Dictionary d = ret[i];
+ ERR_CONTINUE(!d.has("default_value"));
+ r_parameters->push_back(Parameter(PropertyInfo::from_dict(d), d["default_value"]));
+ }
+}
+
void AudioStream::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_length"), &AudioStream::get_length);
ClassDB::bind_method(D_METHOD("is_monophonic"), &AudioStream::is_monophonic);
@@ -259,6 +281,9 @@ void AudioStream::_bind_methods() {
GDVIRTUAL_BIND(_is_monophonic);
GDVIRTUAL_BIND(_get_bpm)
GDVIRTUAL_BIND(_get_beat_count)
+ GDVIRTUAL_BIND(_get_parameter_list)
+
+ ADD_SIGNAL(MethodInfo("parameter_list_changed"));
}
////////////////////////////////
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 015e89fc8e..f8123fbe15 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -38,6 +38,7 @@
#include "core/object/gdvirtual.gen.inc"
#include "core/variant/native_ptr.h"
+#include "core/variant/typed_array.h"
class AudioStream;
@@ -54,6 +55,9 @@ protected:
GDVIRTUAL1(_seek, double)
GDVIRTUAL3R(int, _mix, GDExtensionPtr<AudioFrame>, float, int)
GDVIRTUAL0(_tag_used_streams)
+ GDVIRTUAL2(_set_parameter, const StringName &, const Variant &)
+ GDVIRTUAL1RC(Variant, _get_parameter, const StringName &)
+
public:
virtual void start(double p_from_pos = 0.0);
virtual void stop();
@@ -66,6 +70,9 @@ public:
virtual void tag_used_streams();
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value);
+ virtual Variant get_parameter(const StringName &p_name) const;
+
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
};
@@ -124,6 +131,7 @@ protected:
GDVIRTUAL0RC(bool, _has_loop)
GDVIRTUAL0RC(int, _get_bar_beats)
GDVIRTUAL0RC(int, _get_beat_count)
+ GDVIRTUAL0RC(TypedArray<Dictionary>, _get_parameter_list)
public:
virtual Ref<AudioStreamPlayback> instantiate_playback();
@@ -141,6 +149,17 @@ public:
uint64_t get_tagged_frame() const;
uint32_t get_tagged_frame_count() const;
float get_tagged_frame_offset(int p_index) const;
+
+ struct Parameter {
+ PropertyInfo property;
+ Variant default_value;
+ Parameter(const PropertyInfo &p_info = PropertyInfo(), const Variant &p_default_value = Variant()) {
+ property = p_info;
+ default_value = p_default_value;
+ }
+ };
+
+ virtual void get_parameter_list(List<Parameter> *r_parameters);
};
// Microphone
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 6875400d1e..2b49d42f9e 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -1340,7 +1340,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
if (is_screen_texture && !texture_func_returns_data && actions.apply_luminance_multiplier) {
code = "(" + code + " * vec4(vec3(sc_luminance_multiplier), 1.0))";
}
- if (is_normal_roughness_texture) {
+ if (is_normal_roughness_texture && !texture_func_returns_data) {
code = "normal_roughness_compatibility(" + code + ")";
}
} break;