Rework editor docks

This commit is contained in:
kobewi 2025-05-16 21:44:43 +02:00
parent 9283328fe7
commit 97b398cba1
26 changed files with 806 additions and 387 deletions

100
doc/classes/EditorDock.xml Normal file
View file

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorDock" inherits="MarginContainer" experimental="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Dockable container for the editor.
</brief_description>
<description>
EditorDock is a [Container] node that can be docked in one of the editor's dock slots. Docks are added by plugins to provide space for controls related to an [EditorPlugin]. The editor comes with a few built-in docks, such as the Scene dock, FileSystem dock, etc.
You can add a dock by using [method EditorPlugin.add_dock]. The dock can be customized by changing its properties.
[codeblock]
@tool
extends EditorPlugin
# Dock reference.
var dock
# Plugin initialization.
func _enter_tree():
dock = EditorDock.new()
dock.title = "My Dock"
dock.dock_icon = preload("./dock_icon.png")
dock.default_slot = EditorPlugin.DOCK_SLOT_RIGHT_UL
var dock_content = preload("./dock_content.tscn").instantiate()
dock.add_child(dock_content)
add_dock(dock)
# Plugin clean-up.
func _exit_tree():
remove_dock(dock)
dock.queue_free()
dock = null
[/codeblock]
</description>
<tutorials>
<link title="Making plugins">$DOCS_URL/tutorials/plugins/editor/making_plugins.html</link>
</tutorials>
<methods>
<method name="_load_layout_from_config" qualifiers="virtual">
<return type="void" />
<param index="0" name="config" type="ConfigFile" />
<param index="1" name="section" type="String" />
<description>
Implement this method to handle loading this dock's layout. It's equivalent to [method EditorPlugin._set_window_layout]. [param section] is a unique section based on [member layout_key].
</description>
</method>
<method name="_save_layout_to_config" qualifiers="virtual const">
<return type="void" />
<param index="0" name="config" type="ConfigFile" />
<param index="1" name="section" type="String" />
<description>
Implement this method to handle saving this dock's layout. It's equivalent to [method EditorPlugin._get_window_layout]. [param section] is a unique section based on [member layout_key].
</description>
</method>
<method name="_update_layout" qualifiers="virtual">
<return type="void" />
<param index="0" name="layout" type="int" />
<description>
Implement this method to handle the layout switching for this dock. [param layout] is one of the [enum DockLayout] constants.
[codeblock]
_update_layout(layout):
box_container.vertical = (layout == DOCK_LAYOUT_VERTICAL)
[/codeblock]
</description>
</method>
</methods>
<members>
<member name="available_layouts" type="int" setter="set_available_layouts" getter="get_available_layouts" enum="EditorDock.DockLayout" is_bitfield="true" default="1">
The available layouts for this dock, as a bitmask.
If you want to make all layouts available, use [code]available_layouts = DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_HORIZONTAL[/code].
</member>
<member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" overrides="Control" default="true" />
<member name="default_slot" type="int" setter="set_default_slot" getter="get_default_slot" enum="EditorPlugin.DockSlot" default="-1">
The default dock slot used when adding the dock with [method EditorPlugin.add_dock].
After the dock is added, it can be moved to a different slot and the editor will automatically remember its position between sessions. If you remove and re-add the dock, it will be reset to default.
</member>
<member name="dock_icon" type="Texture2D" setter="set_dock_icon" getter="get_dock_icon">
The icon for the dock, as a texture. If specified, it will override [member icon_name].
</member>
<member name="dock_shortcut" type="Shortcut" setter="set_dock_shortcut" getter="get_dock_shortcut">
The shortcut used to open the dock. This property can only be set before this dock is added via [method EditorPlugin.add_dock].
</member>
<member name="icon_name" type="StringName" setter="set_icon_name" getter="get_icon_name" default="&amp;&quot;&quot;">
The icon for the dock, as a name from the [code]EditorIcons[/code] theme type in the editor theme. You can find the list of available icons [url=https://godot-editor-icons.github.io/]here[/url].
</member>
<member name="layout_key" type="String" setter="set_layout_key" getter="get_layout_key" default="&quot;&quot;">
The key representing this dock in the editor's layout file. If empty, the dock's displayed name will be used instead.
</member>
<member name="title" type="String" setter="set_title" getter="get_title" default="&quot;&quot;">
The title of the dock's tab. If empty, the dock's [member Node.name] will be used. If the name is auto-generated (contains [code]@[/code]), the first child's name will be used instead.
</member>
</members>
<constants>
<constant name="DOCK_LAYOUT_VERTICAL" value="1" enum="DockLayout" is_bitfield="true">
Allows placing the dock in the vertical dock slots on either side of the editor.
[b]Note:[/b] Currently this flag has no effect because the bottom panel is not a proper dock slot. This means that the dock can always be vertical.
</constant>
<constant name="DOCK_LAYOUT_HORIZONTAL" value="2" enum="DockLayout" is_bitfield="true">
Allows placing the dock in the editor's bottom panel. Implement [method _update_layout] to handle changing layouts.
</constant>
</constants>
</class>

View file

@ -430,7 +430,7 @@
When your plugin is deactivated, make sure to remove your custom control with [method remove_control_from_container] and free it with [method Node.queue_free].
</description>
</method>
<method name="add_control_to_dock">
<method name="add_control_to_dock" deprecated="Use [method add_dock] instead.">
<return type="void" />
<param index="0" name="slot" type="int" enum="EditorPlugin.DockSlot" />
<param index="1" name="control" type="Control" />
@ -464,6 +464,14 @@
Adds a [Script] as debugger plugin to the Debugger. The script must extend [EditorDebuggerPlugin].
</description>
</method>
<method name="add_dock">
<return type="void" />
<param index="0" name="dock" type="EditorDock" />
<description>
Adds a new dock.
When your plugin is deactivated, make sure to remove your custom dock with [method remove_dock] and free it with [method Node.queue_free].
</description>
</method>
<method name="add_export_platform">
<return type="void" />
<param index="0" name="platform" type="EditorExportPlatform" />
@ -655,7 +663,7 @@
Removes the control from the specified container. You have to manually [method Node.queue_free] the control.
</description>
</method>
<method name="remove_control_from_docks">
<method name="remove_control_from_docks" deprecated="Use [method remove_dock] instead.">
<return type="void" />
<param index="0" name="control" type="Control" />
<description>
@ -676,6 +684,13 @@
Removes the debugger plugin with given script from the Debugger.
</description>
</method>
<method name="remove_dock">
<return type="void" />
<param index="0" name="dock" type="EditorDock" />
<description>
Removes [param dock] from the available docks. You should manually call [method Node.queue_free] to free it.
</description>
</method>
<method name="remove_export_platform">
<return type="void" />
<param index="0" name="platform" type="EditorExportPlatform" />
@ -753,7 +768,7 @@
Removes a callback previously added by [method add_undo_redo_inspector_hook_callback].
</description>
</method>
<method name="set_dock_tab_icon">
<method name="set_dock_tab_icon" deprecated="Use [member EditorDock.dock_icon] instead.">
<return type="void" />
<param index="0" name="control" type="Control" />
<param index="1" name="icon" type="Texture2D" />
@ -854,6 +869,9 @@
<constant name="CONTAINER_PROJECT_SETTING_TAB_RIGHT" value="11" enum="CustomControlContainer">
Tab of Project Settings dialog, to the right of other tabs.
</constant>
<constant name="DOCK_SLOT_NONE" value="-1" enum="DockSlot">
The dock is closed.
</constant>
<constant name="DOCK_SLOT_LEFT_UL" value="0" enum="DockSlot">
Dock slot, left side, upper-left (empty in default layout).
</constant>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FileSystemDock" inherits="VBoxContainer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<class name="FileSystemDock" inherits="EditorDock" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Godot editor's dock for managing files in the project.
</brief_description>

View file

@ -0,0 +1,131 @@
/**************************************************************************/
/* editor_dock.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.h"
#include "core/input/shortcut.h"
#include "core/io/config_file.h"
void EditorDock::_set_default_slot_bind(EditorPlugin::DockSlot p_slot) {
ERR_FAIL_COND(p_slot < EditorPlugin::DOCK_SLOT_NONE || p_slot >= EditorPlugin::DOCK_SLOT_MAX);
default_slot = (EditorDockManager::DockSlot)p_slot;
}
void EditorDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &EditorDock::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &EditorDock::get_title);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
ClassDB::bind_method(D_METHOD("set_layout_key", "layout_key"), &EditorDock::set_layout_key);
ClassDB::bind_method(D_METHOD("get_layout_key"), &EditorDock::get_layout_key);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "layout_key"), "set_layout_key", "get_layout_key");
ClassDB::bind_method(D_METHOD("set_icon_name", "icon_name"), &EditorDock::set_icon_name);
ClassDB::bind_method(D_METHOD("get_icon_name"), &EditorDock::get_icon_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "icon_name"), "set_icon_name", "get_icon_name");
ClassDB::bind_method(D_METHOD("set_dock_icon", "icon"), &EditorDock::set_dock_icon);
ClassDB::bind_method(D_METHOD("get_dock_icon"), &EditorDock::get_dock_icon);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "dock_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_dock_icon", "get_dock_icon");
ClassDB::bind_method(D_METHOD("set_dock_shortcut", "shortcut"), &EditorDock::set_dock_shortcut);
ClassDB::bind_method(D_METHOD("get_dock_shortcut"), &EditorDock::get_dock_shortcut);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "dock_shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_dock_shortcut", "get_dock_shortcut");
ClassDB::bind_method(D_METHOD("set_default_slot", "slot"), &EditorDock::_set_default_slot_bind);
ClassDB::bind_method(D_METHOD("get_default_slot"), &EditorDock::_get_default_slot_bind);
ADD_PROPERTY(PropertyInfo(Variant::INT, "default_slot"), "set_default_slot", "get_default_slot");
ClassDB::bind_method(D_METHOD("set_available_layouts", "layouts"), &EditorDock::set_available_layouts);
ClassDB::bind_method(D_METHOD("get_available_layouts"), &EditorDock::get_available_layouts);
ADD_PROPERTY(PropertyInfo(Variant::INT, "available_layouts", PROPERTY_HINT_FLAGS, "Vertical:1,Horizontal:2"), "set_available_layouts", "get_available_layouts");
BIND_BITFIELD_FLAG(DOCK_LAYOUT_VERTICAL);
BIND_BITFIELD_FLAG(DOCK_LAYOUT_HORIZONTAL);
GDVIRTUAL_BIND(_update_layout, "layout");
GDVIRTUAL_BIND(_save_layout_to_config, "config", "section");
GDVIRTUAL_BIND(_load_layout_from_config, "config", "section");
}
EditorDock::EditorDock() {
set_clip_contents(true);
add_user_signal(MethodInfo("tab_style_changed"));
}
void EditorDock::set_title(const String &p_title) {
if (title == p_title) {
return;
}
title = p_title;
emit_signal("tab_style_changed");
}
void EditorDock::set_icon_name(const StringName &p_name) {
if (icon_name == p_name) {
return;
}
icon_name = p_name;
emit_signal("tab_style_changed");
}
void EditorDock::set_dock_icon(const Ref<Texture2D> &p_icon) {
if (dock_icon == p_icon) {
return;
}
dock_icon = p_icon;
emit_signal("tab_style_changed");
}
void EditorDock::set_default_slot(EditorDockManager::DockSlot p_slot) {
ERR_FAIL_INDEX(p_slot, EditorDockManager::DOCK_SLOT_MAX);
default_slot = p_slot;
}
String EditorDock::get_display_title() const {
if (!title.is_empty()) {
return title;
}
const String sname = get_name();
if (sname.contains_char('@')) {
// Auto-generated name, try to use something better.
const Node *child = get_child_count() > 0 ? get_child(0) : nullptr;
if (child) {
// In user plugins, the child will usually be dock's content and have a proper name.
return child->get_name();
}
}
return sname;
}
String EditorDock::get_effective_layout_key() const {
return layout_key.is_empty() ? get_display_title() : layout_key;
}

113
editor/docks/editor_dock.h Normal file
View file

@ -0,0 +1,113 @@
/**************************************************************************/
/* editor_dock.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. */
/**************************************************************************/
#pragma once
#include "editor/docks/editor_dock_manager.h"
#include "editor/plugins/editor_plugin.h"
#include "scene/gui/margin_container.h"
class ConfigFile;
class Shortcut;
class WindowWrapper;
class EditorDock : public MarginContainer {
GDCLASS(EditorDock, MarginContainer);
public:
enum DockLayout {
DOCK_LAYOUT_VERTICAL = 1,
DOCK_LAYOUT_HORIZONTAL = 2,
};
private:
friend class EditorDockManager;
friend class DockContextPopup;
String title;
String layout_key;
StringName icon_name;
Ref<Texture2D> dock_icon;
Ref<Shortcut> shortcut;
EditorDockManager::DockSlot default_slot = EditorDockManager::DOCK_SLOT_NONE;
BitField<DockLayout> available_layouts = DOCK_LAYOUT_VERTICAL;
bool open = false;
bool enabled = true;
bool at_bottom = false;
int previous_tab_index = -1;
bool previous_at_bottom = false;
WindowWrapper *dock_window = nullptr;
int dock_slot_index = EditorDockManager::DOCK_SLOT_NONE;
void _set_default_slot_bind(EditorPlugin::DockSlot p_slot);
EditorPlugin::DockSlot _get_default_slot_bind() const { return (EditorPlugin::DockSlot)default_slot; }
protected:
static void _bind_methods();
GDVIRTUAL1(_update_layout, int)
GDVIRTUAL2C(_save_layout_to_config, Ref<ConfigFile>, const String &)
GDVIRTUAL2(_load_layout_from_config, Ref<ConfigFile>, const String &)
public:
EditorDock();
void set_title(const String &p_title);
String get_title() const { return title; }
void set_layout_key(const String &p_key) { layout_key = p_key; }
String get_layout_key() const { return layout_key; }
void set_icon_name(const StringName &p_name);
StringName get_icon_name() const { return icon_name; }
void set_dock_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_dock_icon() const { return dock_icon; }
void set_dock_shortcut(const Ref<Shortcut> &p_shortcut) { shortcut = p_shortcut; }
Ref<Shortcut> get_dock_shortcut() const { return shortcut; }
void set_default_slot(EditorDockManager::DockSlot p_slot);
EditorDockManager::DockSlot get_default_slot() const { return default_slot; }
void set_available_layouts(BitField<DockLayout> p_layouts) { available_layouts = p_layouts; }
BitField<DockLayout> get_available_layouts() const { return available_layouts; }
String get_display_title() const;
String get_effective_layout_key() const;
virtual void update_layout(DockLayout p_layout) { GDVIRTUAL_CALL(_update_layout, p_layout); }
virtual void save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const { GDVIRTUAL_CALL(_save_layout_to_config, p_layout, p_section); }
virtual void load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) { GDVIRTUAL_CALL(_load_layout_from_config, p_layout, p_section); }
};
VARIANT_BITFIELD_CAST(EditorDock::DockLayout);

View file

@ -37,6 +37,7 @@
#include "scene/gui/tab_container.h"
#include "scene/main/window.h"
#include "editor/docks/editor_dock.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_bottom_panel.h"
@ -51,7 +52,13 @@ enum class TabStyle {
TEXT_AND_ICON,
};
EditorDockManager *EditorDockManager::singleton = nullptr;
static inline Ref<Texture2D> _get_dock_icon(const EditorDock *p_dock, const Callable &p_icon_fetch) {
Ref<Texture2D> icon = p_dock->get_dock_icon();
if (icon.is_null() && !p_dock->get_icon_name().is_empty()) {
icon = p_icon_fetch.call(p_dock->get_icon_name());
}
return icon;
}
bool EditorDockDragHint::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
return can_drop_dock;
@ -233,7 +240,7 @@ DockSplitContainer::DockSplitContainer() {
////////////////////////////////////////////////
////////////////////////////////////////////////
Control *EditorDockManager::_get_dock_tab_dragged() {
EditorDock *EditorDockManager::_get_dock_tab_dragged() {
if (dock_tab_dragged) {
return dock_tab_dragged;
}
@ -243,23 +250,17 @@ Control *EditorDockManager::_get_dock_tab_dragged() {
// Check if we are dragging a dock.
const String type = dock_drop_data.get("type", "");
if (type == "tab_container_tab") {
Node *from_node = dock_slot[DOCK_SLOT_LEFT_BL]->get_node(dock_drop_data["from_path"]);
if (!from_node) {
Node *source_tab_bar = EditorNode::get_singleton()->get_node(dock_drop_data["from_path"]);
if (!source_tab_bar) {
return nullptr;
}
TabContainer *parent = Object::cast_to<TabContainer>(from_node->get_parent());
if (!parent) {
TabContainer *source_tab_container = Object::cast_to<TabContainer>(source_tab_bar->get_parent());
if (!source_tab_container) {
return nullptr;
}
// TODO: Update logic when GH-106503 is merged to cast directly to EditorDock instead of the below check.
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
if (dock_slot[i] == parent) {
dock_tab_dragged = parent->get_tab_control(dock_drop_data["tab_index"]);
break;
}
}
dock_tab_dragged = Object::cast_to<EditorDock>(source_tab_container->get_tab_control(dock_drop_data["tab_index"]));
if (!dock_tab_dragged) {
return nullptr;
}
@ -294,13 +295,13 @@ void EditorDockManager::_dock_container_gui_input(const Ref<InputEvent> &p_input
}
// Right click context menu.
dock_context_popup->set_dock(p_dock_container->get_tab_control(tab_id));
dock_context_popup->set_dock(Object::cast_to<EditorDock>(p_dock_container->get_tab_control(tab_id)));
dock_context_popup->set_position(p_dock_container->get_screen_position() + mb->get_position());
dock_context_popup->popup();
}
}
void EditorDockManager::_bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, Control *p_dock, Button *p_bottom_button) {
void EditorDockManager::_bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, EditorDock *p_dock, Button *p_bottom_button) {
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
@ -337,38 +338,42 @@ void EditorDockManager::update_docks_menu() {
bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && NativeMenu::get_singleton()->has_feature(NativeMenu::FEATURE_GLOBAL_MENU);
bool dark_mode = DisplayServer::get_singleton()->is_dark_mode_supported() && DisplayServer::get_singleton()->is_dark_mode();
int icon_max_width = EditorNode::get_singleton()->get_editor_theme()->get_constant(SNAME("class_icon_size"), EditorStringName(Editor));
// Add docks.
docks_menu_docks.clear();
int id = 0;
for (const KeyValue<Control *, DockInfo> &dock : all_docks) {
if (!dock.value.enabled) {
const Callable icon_fetch = callable_mp((Window *)docks_menu, &Window::get_editor_theme_native_menu_icon).bind(global_menu, dark_mode);
for (EditorDock *dock : all_docks) {
if (!dock->enabled) {
continue;
}
if (dock.value.shortcut.is_valid()) {
docks_menu->add_shortcut(dock.value.shortcut, id);
docks_menu->set_item_text(id, dock.value.title);
if (dock->shortcut.is_valid()) {
docks_menu->add_shortcut(dock->shortcut, id);
docks_menu->set_item_text(id, dock->get_display_title());
} else {
docks_menu->add_item(dock.value.title, id);
docks_menu->add_item(dock->get_display_title(), id);
}
const Ref<Texture2D> icon = dock.value.icon_name ? docks_menu->get_editor_theme_native_menu_icon(dock.value.icon_name, global_menu, dark_mode) : dock.value.icon;
docks_menu->set_item_icon_max_width(id, icon_max_width);
const Ref<Texture2D> icon = _get_dock_icon(dock, icon_fetch);
docks_menu->set_item_icon(id, icon.is_valid() ? icon : default_icon);
if (!dock.value.open) {
if (!dock->open) {
docks_menu->set_item_icon_modulate(id, closed_icon_color_mod);
docks_menu->set_item_tooltip(id, vformat(TTR("Open the %s dock."), dock.value.title));
docks_menu->set_item_tooltip(id, vformat(TTR("Open the %s dock."), dock->get_display_title()));
} else {
docks_menu->set_item_tooltip(id, vformat(TTR("Focus on the %s dock."), dock.value.title));
docks_menu->set_item_tooltip(id, vformat(TTR("Focus on the %s dock."), dock->get_display_title()));
}
docks_menu_docks.push_back(dock.key);
docks_menu_docks.push_back(dock);
id++;
}
}
void EditorDockManager::_docks_menu_option(int p_id) {
Control *dock = docks_menu_docks[p_id];
EditorDock *dock = docks_menu_docks[p_id];
ERR_FAIL_NULL(dock);
ERR_FAIL_COND_MSG(!all_docks.has(dock), vformat("Menu option for unknown dock '%s'.", dock->get_name()));
if (all_docks[dock].enabled && all_docks[dock].open) {
ERR_FAIL_COND_MSG(!all_docks.has(dock), vformat("Menu option for unknown dock '%s'.", dock->get_display_title()));
if (dock->enabled && dock->open) {
PopupMenu *parent_menu = Object::cast_to<PopupMenu>(docks_menu->get_parent());
ERR_FAIL_NULL(parent_menu);
parent_menu->hide();
@ -378,11 +383,11 @@ void EditorDockManager::_docks_menu_option(int p_id) {
void EditorDockManager::_window_close_request(WindowWrapper *p_wrapper) {
// Give the dock back to the original owner.
Control *dock = _close_window(p_wrapper);
EditorDock *dock = _close_window(p_wrapper);
ERR_FAIL_COND(!all_docks.has(dock));
if (all_docks[dock].previous_at_bottom || all_docks[dock].dock_slot_index != DOCK_SLOT_NONE) {
all_docks[dock].open = false;
if (dock->previous_at_bottom || dock->dock_slot_index != DOCK_SLOT_NONE) {
dock->open = false;
open_dock(dock);
focus_dock(dock);
} else {
@ -390,19 +395,19 @@ void EditorDockManager::_window_close_request(WindowWrapper *p_wrapper) {
}
}
Control *EditorDockManager::_close_window(WindowWrapper *p_wrapper) {
EditorDock *EditorDockManager::_close_window(WindowWrapper *p_wrapper) {
p_wrapper->set_block_signals(true);
Control *dock = p_wrapper->release_wrapped_control();
EditorDock *dock = Object::cast_to<EditorDock>(p_wrapper->release_wrapped_control());
p_wrapper->set_block_signals(false);
ERR_FAIL_COND_V(!all_docks.has(dock), nullptr);
all_docks[dock].dock_window = nullptr;
dock->dock_window = nullptr;
dock_windows.erase(p_wrapper);
p_wrapper->queue_free();
return dock;
}
void EditorDockManager::_open_dock_in_window(Control *p_dock, bool p_show_window, bool p_reset_size) {
void EditorDockManager::_open_dock_in_window(EditorDock *p_dock, bool p_show_window, bool p_reset_size) {
ERR_FAIL_NULL(p_dock);
Size2 borders = Size2(4, 4) * EDSCALE;
@ -411,7 +416,7 @@ void EditorDockManager::_open_dock_in_window(Control *p_dock, bool p_show_window
Point2 dock_screen_pos = p_dock->get_screen_position();
WindowWrapper *wrapper = memnew(WindowWrapper);
wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), all_docks[p_dock].title));
wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), p_dock->get_display_title()));
wrapper->set_margins_enabled(true);
EditorNode::get_singleton()->get_gui_base()->add_child(wrapper);
@ -419,8 +424,8 @@ void EditorDockManager::_open_dock_in_window(Control *p_dock, bool p_show_window
_move_dock(p_dock, nullptr);
wrapper->set_wrapped_control(p_dock);
all_docks[p_dock].dock_window = wrapper;
all_docks[p_dock].open = true;
p_dock->dock_window = wrapper;
p_dock->open = true;
p_dock->show();
wrapper->connect("window_close_requested", callable_mp(this, &EditorDockManager::_window_close_request).bind(wrapper));
@ -439,45 +444,44 @@ void EditorDockManager::_open_dock_in_window(Control *p_dock, bool p_show_window
}
}
void EditorDockManager::_restore_dock_to_saved_window(Control *p_dock, const Dictionary &p_window_dump) {
if (!all_docks[p_dock].dock_window) {
void EditorDockManager::_restore_dock_to_saved_window(EditorDock *p_dock, const Dictionary &p_window_dump) {
if (!p_dock->dock_window) {
_open_dock_in_window(p_dock, false);
}
all_docks[p_dock].dock_window->restore_window_from_saved_position(
p_dock->dock_window->restore_window_from_saved_position(
p_window_dump.get("window_rect", Rect2i()),
p_window_dump.get("window_screen", -1),
p_window_dump.get("window_screen_rect", Rect2i()));
}
void EditorDockManager::_dock_move_to_bottom(Control *p_dock, bool p_visible) {
void EditorDockManager::_dock_move_to_bottom(EditorDock *p_dock, bool p_visible) {
_move_dock(p_dock, nullptr);
all_docks[p_dock].at_bottom = true;
all_docks[p_dock].previous_at_bottom = false;
p_dock->call("_set_dock_horizontal", true);
p_dock->at_bottom = true;
p_dock->previous_at_bottom = false;
p_dock->update_layout(EditorDock::DOCK_LAYOUT_HORIZONTAL);
// Force docks moved to the bottom to appear first in the list, and give them their associated shortcut to toggle their bottom panel.
Button *bottom_button = EditorNode::get_bottom_panel()->add_item(all_docks[p_dock].title, p_dock, all_docks[p_dock].shortcut, true);
Button *bottom_button = EditorNode::get_bottom_panel()->add_item(p_dock->get_display_title(), p_dock, p_dock->shortcut, true);
bottom_button->connect(SceneStringName(gui_input), callable_mp(this, &EditorDockManager::_bottom_dock_button_gui_input).bind(bottom_button).bind(p_dock));
EditorNode::get_bottom_panel()->make_item_visible(p_dock, p_visible);
}
void EditorDockManager::_dock_remove_from_bottom(Control *p_dock) {
all_docks[p_dock].at_bottom = false;
all_docks[p_dock].previous_at_bottom = true;
void EditorDockManager::_dock_remove_from_bottom(EditorDock *p_dock) {
p_dock->at_bottom = false;
p_dock->previous_at_bottom = true;
EditorNode::get_bottom_panel()->remove_item(p_dock);
p_dock->call("_set_dock_horizontal", false);
p_dock->update_layout(EditorDock::DOCK_LAYOUT_VERTICAL);
}
bool EditorDockManager::_is_dock_at_bottom(Control *p_dock) {
bool EditorDockManager::_is_dock_at_bottom(EditorDock *p_dock) {
ERR_FAIL_COND_V(!all_docks.has(p_dock), false);
return all_docks[p_dock].at_bottom;
return p_dock->at_bottom;
}
void EditorDockManager::_move_dock_tab_index(Control *p_dock, int p_tab_index, bool p_set_current) {
void EditorDockManager::_move_dock_tab_index(EditorDock *p_dock, int p_tab_index, bool p_set_current) {
TabContainer *dock_tab_container = Object::cast_to<TabContainer>(p_dock->get_parent());
if (!dock_tab_container) {
return;
@ -486,7 +490,7 @@ void EditorDockManager::_move_dock_tab_index(Control *p_dock, int p_tab_index, b
dock_tab_container->set_block_signals(true);
int target_index = CLAMP(p_tab_index, 0, dock_tab_container->get_tab_count() - 1);
dock_tab_container->move_child(p_dock, dock_tab_container->get_tab_control(target_index)->get_index(false));
all_docks[p_dock].previous_tab_index = target_index;
p_dock->previous_tab_index = target_index;
if (p_set_current) {
dock_tab_container->set_current_tab(target_index);
@ -494,9 +498,9 @@ void EditorDockManager::_move_dock_tab_index(Control *p_dock, int p_tab_index, b
dock_tab_container->set_block_signals(false);
}
void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab_index, bool p_set_current) {
void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_tab_index, bool p_set_current) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot move unknown dock '%s'.", p_dock->get_name()));
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot move unknown dock '%s'.", p_dock->get_display_title()));
Node *parent = p_dock->get_parent();
if (parent == p_target) {
@ -509,15 +513,15 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
// Remove dock from its existing parent.
if (parent) {
if (all_docks[p_dock].dock_window) {
_close_window(all_docks[p_dock].dock_window);
} else if (all_docks[p_dock].at_bottom) {
if (p_dock->dock_window) {
_close_window(p_dock->dock_window);
} else if (p_dock->at_bottom) {
_dock_remove_from_bottom(p_dock);
} else {
all_docks[p_dock].previous_at_bottom = false;
p_dock->previous_at_bottom = false;
TabContainer *parent_tabs = Object::cast_to<TabContainer>(parent);
if (parent_tabs) {
all_docks[p_dock].previous_tab_index = parent_tabs->get_tab_idx_from_control(p_dock);
p_dock->previous_tab_index = parent_tabs->get_tab_idx_from_control(p_dock);
}
parent->set_block_signals(true);
parent->remove_child(p_dock);
@ -547,36 +551,35 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
}
}
void EditorDockManager::_update_tab_style(Control *p_dock) {
const DockInfo &dock_info = all_docks[p_dock];
if (!dock_info.enabled || !dock_info.open) {
void EditorDockManager::_update_tab_style(EditorDock *p_dock) {
if (!p_dock->enabled || !p_dock->open) {
return; // Disabled by feature profile or manually closed by user.
}
if (dock_info.dock_window || dock_info.at_bottom) {
if (p_dock->dock_window || p_dock->at_bottom) {
return; // Floating or sent to bottom.
}
TabContainer *tab_container = get_dock_tab_container(p_dock);
ERR_FAIL_NULL(tab_container);
int index = tab_container->get_tab_idx_from_control(p_dock);
ERR_FAIL_COND(index == -1);
const TabStyle style = (TabStyle)EDITOR_GET("interface/editor/dock_tab_style").operator int();
const Ref<Texture2D> icon = _get_dock_icon(p_dock, callable_mp((Control *)tab_container, &Control::get_editor_theme_icon));
switch (style) {
case TabStyle::TEXT_ONLY: {
tab_container->set_tab_title(index, dock_info.title);
tab_container->set_tab_title(index, p_dock->get_display_title());
tab_container->set_tab_icon(index, Ref<Texture2D>());
tab_container->set_tab_tooltip(index, String());
} break;
case TabStyle::ICON_ONLY: {
const Ref<Texture2D> icon = dock_info.icon_name ? tab_container->get_editor_theme_icon(dock_info.icon_name) : dock_info.icon;
tab_container->set_tab_title(index, icon.is_valid() ? String() : dock_info.title);
tab_container->set_tab_title(index, icon.is_valid() ? String() : p_dock->get_display_title());
tab_container->set_tab_icon(index, icon);
tab_container->set_tab_tooltip(index, icon.is_valid() ? dock_info.title : String());
tab_container->set_tab_tooltip(index, p_dock->get_display_title());
} break;
case TabStyle::TEXT_AND_ICON: {
const Ref<Texture2D> icon = dock_info.icon_name ? tab_container->get_editor_theme_icon(dock_info.icon_name) : dock_info.icon;
tab_container->set_tab_title(index, dock_info.title);
tab_container->set_tab_title(index, p_dock->get_display_title());
tab_container->set_tab_icon(index, icon);
tab_container->set_tab_tooltip(index, String());
} break;
@ -588,7 +591,7 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
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();
String name = Object::cast_to<EditorDock>(dock_slot[i]->get_tab_control(j))->get_effective_layout_key();
if (!names.is_empty()) {
names += ",";
}
@ -618,7 +621,7 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
// Save docks in windows.
Dictionary floating_docks_dump;
for (WindowWrapper *wrapper : dock_windows) {
Control *dock = wrapper->get_wrapped_control();
EditorDock *dock = Object::cast_to<EditorDock>(wrapper->get_wrapped_control());
Dictionary window_dump;
window_dump["window_rect"] = wrapper->get_window_rect();
@ -627,11 +630,11 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
window_dump["window_screen"] = wrapper->get_window_screen();
window_dump["window_screen_rect"] = DisplayServer::get_singleton()->screen_get_usable_rect(screen);
String name = dock->get_name();
String name = dock->get_effective_layout_key();
floating_docks_dump[name] = window_dump;
// Append to regular dock section so we know where to restore it to.
int dock_slot_id = all_docks[dock].dock_slot_index;
int dock_slot_id = dock->dock_slot_index;
String config_key = "dock_" + itos(dock_slot_id + 1);
String names = p_layout->get_value(p_section, config_key, "");
@ -647,22 +650,23 @@ void EditorDockManager::save_docks_to_config(Ref<ConfigFile> p_layout, const Str
// Save closed and bottom docks.
Array bottom_docks_dump;
Array closed_docks_dump;
for (const KeyValue<Control *, DockInfo> &d : all_docks) {
d.key->call(SNAME("_save_layout_to_config"), p_layout, p_section);
for (const EditorDock *dock : all_docks) {
const String section_name = p_section + "/" + dock->get_effective_layout_key();
dock->save_layout_to_config(p_layout, section_name);
if (!d.value.at_bottom && d.value.open && (!d.value.previous_at_bottom || !d.value.dock_window)) {
if (!dock->at_bottom && dock->open && (!dock->previous_at_bottom || !dock->dock_window)) {
continue;
}
// Use the name of the Control since it isn't translated.
String name = d.key->get_name();
if (d.value.at_bottom || (d.value.previous_at_bottom && d.value.dock_window)) {
const String name = dock->get_effective_layout_key();
if (dock->at_bottom || (dock->previous_at_bottom && dock->dock_window)) {
bottom_docks_dump.push_back(name);
}
if (!d.value.open) {
if (!dock->open) {
closed_docks_dump.push_back(name);
}
int dock_slot_id = all_docks[d.key].dock_slot_index;
int dock_slot_id = dock->dock_slot_index;
String config_key = "dock_" + itos(dock_slot_id + 1);
String names = p_layout->get_value(p_section, config_key, "");
@ -696,9 +700,9 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
bool allow_floating_docks = EditorNode::get_singleton()->is_multi_window_enabled() && (!p_first_load || EDITOR_GET("interface/multi_window/restore_windows_on_load"));
// Store the docks by name for easy lookup.
HashMap<String, Control *> dock_map;
for (const KeyValue<Control *, DockInfo> &dock : all_docks) {
dock_map[dock.key->get_name()] = dock.key;
HashMap<String, EditorDock *> dock_map;
for (EditorDock *dock : all_docks) {
dock_map[dock->get_effective_layout_key()] = dock;
}
// Load docks by slot. Index -1 is for docks that have no slot.
@ -710,21 +714,22 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
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];
const String &name = names[j];
const String section_name = p_section + "/" + name;
if (!dock_map.has(name)) {
continue;
}
Control *dock = dock_map[name];
EditorDock *dock = dock_map[name];
if (!all_docks[dock].enabled) {
if (!dock->enabled) {
// Don't open disabled docks.
dock->call(SNAME("_load_layout_from_config"), p_layout, p_section);
dock->load_layout_from_config(p_layout, section_name);
continue;
}
bool at_bottom = false;
if (allow_floating_docks && floating_docks_dump.has(name)) {
all_docks[dock].previous_at_bottom = dock_bottom.has(name);
dock->previous_at_bottom = dock_bottom.has(name);
_restore_dock_to_saved_window(dock, floating_docks_dump[name]);
} else if (dock_bottom.has(name)) {
_dock_move_to_bottom(dock, false);
@ -732,15 +737,15 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
} else if (i >= 0) {
_move_dock(dock, dock_slot[i], 0);
}
dock->call(SNAME("_load_layout_from_config"), p_layout, p_section);
dock->load_layout_from_config(p_layout, section_name);
if (closed_docks.has(name)) {
_move_dock(dock, closed_dock_parent);
all_docks[dock].open = false;
dock->open = false;
dock->hide();
} else {
// Make sure it is open.
all_docks[dock].open = true;
dock->open = true;
// It's important to not update the visibility of bottom panels.
// Visibility of bottom panels are managed in EditorBottomPanel.
if (!at_bottom) {
@ -748,8 +753,8 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
}
}
all_docks[dock].dock_slot_index = i;
all_docks[dock].previous_tab_index = i >= 0 ? j : 0;
dock->dock_slot_index = i;
dock->previous_tab_index = i >= 0 ? j : 0;
}
}
@ -785,7 +790,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
update_docks_menu();
}
void EditorDockManager::bottom_dock_show_placement_popup(const Rect2i &p_position, Control *p_dock) {
void EditorDockManager::bottom_dock_show_placement_popup(const Rect2i &p_position, EditorDock *p_dock) {
ERR_FAIL_COND(!all_docks.has(p_dock));
dock_context_popup->set_dock(p_dock);
@ -801,15 +806,15 @@ void EditorDockManager::bottom_dock_show_placement_popup(const Rect2i &p_positio
dock_context_popup->popup();
}
void EditorDockManager::set_dock_enabled(Control *p_dock, bool p_enabled) {
void EditorDockManager::set_dock_enabled(EditorDock *p_dock, bool p_enabled) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot set enabled unknown dock '%s'.", p_dock->get_name()));
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot set enabled unknown dock '%s'.", p_dock->get_display_title()));
if (all_docks[p_dock].enabled == p_enabled) {
if (p_dock->enabled == p_enabled) {
return;
}
all_docks[p_dock].enabled = p_enabled;
p_dock->enabled = p_enabled;
if (p_enabled) {
open_dock(p_dock, false);
} else {
@ -817,39 +822,39 @@ void EditorDockManager::set_dock_enabled(Control *p_dock, bool p_enabled) {
}
}
void EditorDockManager::close_dock(Control *p_dock) {
void EditorDockManager::close_dock(EditorDock *p_dock) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot close unknown dock '%s'.", p_dock->get_name()));
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot close unknown dock '%s'.", p_dock->get_display_title()));
if (!all_docks[p_dock].open) {
if (!p_dock->open) {
return;
}
_move_dock(p_dock, closed_dock_parent);
all_docks[p_dock].open = false;
p_dock->open = false;
p_dock->hide();
_update_layout();
}
void EditorDockManager::open_dock(Control *p_dock, bool p_set_current) {
void EditorDockManager::open_dock(EditorDock *p_dock, bool p_set_current) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot open unknown dock '%s'.", p_dock->get_name()));
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot open unknown dock '%s'.", p_dock->get_display_title()));
if (all_docks[p_dock].open) {
if (p_dock->open) {
return;
}
all_docks[p_dock].open = true;
p_dock->open = true;
p_dock->show();
// Open dock to its previous location.
if (all_docks[p_dock].previous_at_bottom) {
if (p_dock->previous_at_bottom) {
_dock_move_to_bottom(p_dock, true);
} else if (all_docks[p_dock].dock_slot_index != DOCK_SLOT_NONE) {
TabContainer *slot = dock_slot[all_docks[p_dock].dock_slot_index];
int tab_index = all_docks[p_dock].previous_tab_index;
} else if (p_dock->dock_slot_index != DOCK_SLOT_NONE) {
TabContainer *slot = dock_slot[p_dock->dock_slot_index];
int tab_index = p_dock->previous_tab_index;
if (tab_index < 0) {
tab_index = slot->get_tab_count();
}
@ -866,24 +871,24 @@ TabContainer *EditorDockManager::get_dock_tab_container(Control *p_dock) const {
return Object::cast_to<TabContainer>(p_dock->get_parent());
}
void EditorDockManager::focus_dock(Control *p_dock) {
void EditorDockManager::focus_dock(EditorDock *p_dock) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot focus unknown dock '%s'.", p_dock->get_name()));
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot focus unknown dock '%s'.", p_dock->get_display_title()));
if (!all_docks[p_dock].enabled) {
if (!p_dock->enabled) {
return;
}
if (!all_docks[p_dock].open) {
if (!p_dock->open) {
open_dock(p_dock);
}
if (all_docks[p_dock].dock_window) {
if (p_dock->dock_window) {
p_dock->get_window()->grab_focus();
return;
}
if (all_docks[p_dock].at_bottom) {
if (p_dock->at_bottom) {
EditorNode::get_bottom_panel()->make_item_visible(p_dock, true, true);
return;
}
@ -901,19 +906,16 @@ void EditorDockManager::focus_dock(Control *p_dock) {
tab_container->set_current_tab(tab_index);
}
void EditorDockManager::add_dock(Control *p_dock, const String &p_title, DockSlot p_slot, const Ref<Shortcut> &p_shortcut, const StringName &p_icon_name) {
void EditorDockManager::add_dock(EditorDock *p_dock) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(all_docks.has(p_dock), vformat("Cannot add dock '%s', already added.", p_dock->get_name()));
ERR_FAIL_COND_MSG(all_docks.has(p_dock), vformat("Cannot add dock '%s', already added.", p_dock->get_display_title()));
DockInfo dock_info;
dock_info.title = p_title.is_empty() ? String(p_dock->get_name()) : p_title;
dock_info.dock_slot_index = p_slot;
dock_info.shortcut = p_shortcut;
dock_info.icon_name = p_icon_name;
all_docks[p_dock] = dock_info;
p_dock->dock_slot_index = p_dock->default_slot;
all_docks.push_back(p_dock);
p_dock->connect("tab_style_changed", callable_mp(this, &EditorDockManager::_update_tab_style).bind(p_dock));
p_dock->connect("renamed", callable_mp(this, &EditorDockManager::_update_tab_style).bind(p_dock));
if (p_slot != DOCK_SLOT_NONE) {
ERR_FAIL_INDEX(p_slot, DOCK_SLOT_MAX);
if (p_dock->default_slot != DOCK_SLOT_NONE) {
open_dock(p_dock, false);
} else {
closed_dock_parent->add_child(p_dock);
@ -922,24 +924,18 @@ void EditorDockManager::add_dock(Control *p_dock, const String &p_title, DockSlo
}
}
void EditorDockManager::remove_dock(Control *p_dock) {
void EditorDockManager::remove_dock(EditorDock *p_dock) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot remove unknown dock '%s'.", p_dock->get_name()));
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot remove unknown dock '%s'.", p_dock->get_display_title()));
_move_dock(p_dock, nullptr);
all_docks.erase(p_dock);
p_dock->disconnect("tab_style_changed", callable_mp(this, &EditorDockManager::_update_tab_style));
p_dock->disconnect("renamed", callable_mp(this, &EditorDockManager::_update_tab_style));
_update_layout();
}
void EditorDockManager::set_dock_tab_icon(Control *p_dock, const Ref<Texture2D> &p_icon) {
ERR_FAIL_NULL(p_dock);
ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot set tab icon for unknown dock '%s'.", p_dock->get_name()));
all_docks[p_dock].icon = p_icon;
_update_tab_style(p_dock);
}
void EditorDockManager::set_docks_visible(bool p_show) {
if (docks_visible == p_show) {
return;
@ -956,8 +952,8 @@ bool EditorDockManager::are_docks_visible() const {
}
void EditorDockManager::update_tab_styles() {
for (const KeyValue<Control *, DockInfo> &dock : all_docks) {
_update_tab_style(dock.key);
for (EditorDock *dock : all_docks) {
_update_tab_style(dock);
}
}
@ -1125,7 +1121,7 @@ void DockContextPopup::_dock_select_input(const Ref<InputEvent> &p_input) {
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
if (dock_manager->get_dock_tab_container(context_dock) != target_tab_container) {
dock_manager->_move_dock(context_dock, target_tab_container, target_tab_container->get_tab_count());
dock_manager->all_docks[context_dock].dock_slot_index = over_dock_slot;
context_dock->dock_slot_index = over_dock_slot;
dock_manager->_update_layout();
hide();
}
@ -1238,21 +1234,21 @@ void DockContextPopup::_update_buttons() {
tab_move_right_button->set_disabled(context_tab_index >= context_tab_container->get_tab_count() - 1);
}
dock_to_bottom_button->set_visible(!dock_at_bottom && bool(context_dock->call("_can_dock_horizontal")));
dock_to_bottom_button->set_visible(!dock_at_bottom && bool(context_dock->available_layouts & EditorDock::DOCK_LAYOUT_HORIZONTAL));
reset_size();
}
void DockContextPopup::select_current_dock_in_dock_slot(int p_dock_slot) {
context_dock = dock_manager->dock_slot[p_dock_slot]->get_current_tab_control();
context_dock = Object::cast_to<EditorDock>(dock_manager->dock_slot[p_dock_slot]->get_current_tab_control());
_update_buttons();
}
void DockContextPopup::set_dock(Control *p_dock) {
void DockContextPopup::set_dock(EditorDock *p_dock) {
context_dock = p_dock;
_update_buttons();
}
Control *DockContextPopup::get_dock() const {
EditorDock *DockContextPopup::get_dock() const {
return context_dock;
}

View file

@ -36,6 +36,7 @@
class Button;
class ConfigFile;
class Control;
class EditorDock;
class PopupMenu;
class TabBar;
class TabContainer;
@ -83,21 +84,7 @@ private:
friend class DockContextPopup;
friend class EditorDockDragHint;
struct DockInfo {
String title;
bool open = false;
bool enabled = true;
bool at_bottom = false;
int previous_tab_index = -1;
bool previous_at_bottom = false;
WindowWrapper *dock_window = nullptr;
int dock_slot_index = DOCK_SLOT_NONE;
Ref<Shortcut> shortcut;
Ref<Texture2D> icon; // Only used when `icon_name` is empty.
StringName icon_name;
};
static EditorDockManager *singleton;
static inline EditorDockManager *singleton = nullptr;
// To access splits easily by index.
Vector<DockSplitContainer *> vsplits;
@ -106,38 +93,38 @@ private:
Vector<WindowWrapper *> dock_windows;
TabContainer *dock_slot[DOCK_SLOT_MAX];
EditorDockDragHint *dock_drag_rects[DOCK_SLOT_MAX];
HashMap<Control *, DockInfo> all_docks;
Control *dock_tab_dragged = nullptr;
LocalVector<EditorDock *> all_docks;
EditorDock *dock_tab_dragged = nullptr;
bool docks_visible = true;
DockContextPopup *dock_context_popup = nullptr;
PopupMenu *docks_menu = nullptr;
Vector<Control *> docks_menu_docks;
LocalVector<EditorDock *> docks_menu_docks;
Control *closed_dock_parent = nullptr;
Control *_get_dock_tab_dragged();
EditorDock *_get_dock_tab_dragged();
void _dock_drag_stopped();
void _dock_split_dragged(int p_offset);
void _dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container);
void _bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, Control *p_dock, Button *p_bottom_button);
void _bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, EditorDock *p_dock, Button *p_bottom_button);
void _dock_container_update_visibility(TabContainer *p_dock_container);
void _update_layout();
void _docks_menu_option(int p_id);
void _window_close_request(WindowWrapper *p_wrapper);
Control *_close_window(WindowWrapper *p_wrapper);
void _open_dock_in_window(Control *p_dock, bool p_show_window = true, bool p_reset_size = false);
void _restore_dock_to_saved_window(Control *p_dock, const Dictionary &p_window_dump);
EditorDock *_close_window(WindowWrapper *p_wrapper);
void _open_dock_in_window(EditorDock *p_dock, bool p_show_window = true, bool p_reset_size = false);
void _restore_dock_to_saved_window(EditorDock *p_dock, const Dictionary &p_window_dump);
void _dock_move_to_bottom(Control *p_dock, bool p_visible);
void _dock_remove_from_bottom(Control *p_dock);
bool _is_dock_at_bottom(Control *p_dock);
void _dock_move_to_bottom(EditorDock *p_dock, bool p_visible);
void _dock_remove_from_bottom(EditorDock *p_dock);
bool _is_dock_at_bottom(EditorDock *p_dock);
void _move_dock_tab_index(Control *p_dock, int p_tab_index, bool p_set_current);
void _move_dock(Control *p_dock, Control *p_target, int p_tab_index = -1, bool p_set_current = true);
void _move_dock_tab_index(EditorDock *p_dock, int p_tab_index, bool p_set_current);
void _move_dock(EditorDock *p_dock, Control *p_target, int p_tab_index = -1, bool p_set_current = true);
void _update_tab_style(Control *p_dock);
void _update_tab_style(EditorDock *p_dock);
public:
static EditorDockManager *get_singleton() { return singleton; }
@ -156,22 +143,20 @@ public:
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, bool p_first_load = false);
void set_dock_enabled(Control *p_dock, bool p_enabled);
void close_dock(Control *p_dock);
void open_dock(Control *p_dock, bool p_set_current = true);
void focus_dock(Control *p_dock);
void set_dock_enabled(EditorDock *p_dock, bool p_enabled);
void close_dock(EditorDock *p_dock);
void open_dock(EditorDock *p_dock, bool p_set_current = true);
void focus_dock(EditorDock *p_dock);
TabContainer *get_dock_tab_container(Control *p_dock) const;
void bottom_dock_show_placement_popup(const Rect2i &p_position, Control *p_dock);
void bottom_dock_show_placement_popup(const Rect2i &p_position, EditorDock *p_dock);
void set_docks_visible(bool p_show);
bool are_docks_visible() const;
void add_dock(Control *p_dock, const String &p_title = "", DockSlot p_slot = DOCK_SLOT_NONE, const Ref<Shortcut> &p_shortcut = nullptr, const StringName &p_icon_name = StringName());
void remove_dock(Control *p_dock);
void set_dock_tab_icon(Control *p_dock, const Ref<Texture2D> &p_icon);
void add_dock(EditorDock *p_dock);
void remove_dock(EditorDock *p_dock);
EditorDockManager();
};
@ -222,7 +207,7 @@ private:
Rect2 dock_select_rects[EditorDockManager::DOCK_SLOT_MAX];
int dock_select_rect_over_idx = -1;
Control *context_dock = nullptr;
EditorDock *context_dock = nullptr;
EditorDockManager *dock_manager = nullptr;
@ -243,8 +228,8 @@ protected:
public:
void select_current_dock_in_dock_slot(int p_dock_slot);
void set_dock(Control *p_dock);
Control *get_dock() const;
void set_dock(EditorDock *p_dock);
EditorDock *get_dock() const;
void docks_updated();
DockContextPopup();

View file

@ -53,11 +53,13 @@
#include "editor/plugins/editor_resource_conversion_plugin.h"
#include "editor/scene/editor_scene_tabs.h"
#include "editor/scene/scene_create_dialog.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_feature_profile.h"
#include "editor/settings/editor_settings.h"
#include "editor/shader/shader_create_dialog.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/box_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
@ -4008,16 +4010,13 @@ MenuButton *FileSystemDock::_create_file_menu_button() {
return button;
}
bool FileSystemDock::_can_dock_horizontal() const {
return true;
}
void FileSystemDock::_set_dock_horizontal(bool p_enable) {
if (button_dock_placement->is_visible() == p_enable) {
void FileSystemDock::update_layout(EditorDock::DockLayout p_layout) {
bool horizontal = p_layout == EditorDock::DOCK_LAYOUT_HORIZONTAL;
if (button_dock_placement->is_visible() == horizontal) {
return;
}
if (p_enable) {
if (horizontal) {
set_meta("_dock_display_mode", get_display_mode());
set_meta("_dock_file_display_mode", get_file_list_display_mode());
@ -4041,75 +4040,49 @@ void FileSystemDock::_set_dock_horizontal(bool p_enable) {
set_file_list_display_mode(new_file_display_mode);
set_custom_minimum_size(Size2(0, 0));
}
button_dock_placement->set_visible(p_enable);
button_dock_placement->set_visible(horizontal);
}
void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("navigate_to_path", "path"), &FileSystemDock::navigate_to_path);
ClassDB::bind_method(D_METHOD("add_resource_tooltip_plugin", "plugin"), &FileSystemDock::add_resource_tooltip_plugin);
ClassDB::bind_method(D_METHOD("remove_resource_tooltip_plugin", "plugin"), &FileSystemDock::remove_resource_tooltip_plugin);
ClassDB::bind_method(D_METHOD("_set_dock_horizontal", "enable"), &FileSystemDock::_set_dock_horizontal);
ClassDB::bind_method(D_METHOD("_can_dock_horizontal"), &FileSystemDock::_can_dock_horizontal);
ClassDB::bind_method(D_METHOD("_save_layout_to_config"), &FileSystemDock::_save_layout_to_config);
ClassDB::bind_method(D_METHOD("_load_layout_from_config"), &FileSystemDock::_load_layout_from_config);
ADD_SIGNAL(MethodInfo("inherit", PropertyInfo(Variant::STRING, "file")));
ADD_SIGNAL(MethodInfo("instantiate", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files")));
ADD_SIGNAL(MethodInfo("resource_removed", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
ADD_SIGNAL(MethodInfo("file_removed", PropertyInfo(Variant::STRING, "file")));
ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder")));
ADD_SIGNAL(MethodInfo("files_moved", PropertyInfo(Variant::STRING, "old_file"), PropertyInfo(Variant::STRING, "new_file")));
ADD_SIGNAL(MethodInfo("folder_moved", PropertyInfo(Variant::STRING, "old_folder"), PropertyInfo(Variant::STRING, "new_folder")));
ADD_SIGNAL(MethodInfo("folder_color_changed"));
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", (int)get_file_sort());
p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", get_file_list_display_mode());
void FileSystemDock::save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const {
p_layout->set_value(p_section, "h_split_offset", get_h_split_offset());
p_layout->set_value(p_section, "v_split_offset", get_v_split_offset());
p_layout->set_value(p_section, "display_mode", get_display_mode());
p_layout->set_value(p_section, "file_sort", (int)get_file_sort());
p_layout->set_value(p_section, "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);
p_layout->set_value(p_section, "selected_paths", selected_files);
Vector<String> uncollapsed_paths = get_uncollapsed_paths();
p_layout->set_value(p_section, "dock_filesystem_uncollapsed_paths", searched_tokens.is_empty() ? uncollapsed_paths : uncollapsed_paths_before_search);
p_layout->set_value(p_section, "uncollapsed_paths", searched_tokens.is_empty() ? uncollapsed_paths : uncollapsed_paths_before_search);
}
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");
void FileSystemDock::load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) {
if (p_layout->has_section_key(p_section, "h_split_offset")) {
int fs_h_split_ofs = p_layout->get_value(p_section, "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");
if (p_layout->has_section_key(p_section, "v_split_offset")) {
int fs_v_split_ofs = p_layout->get_value(p_section, "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")));
if (p_layout->has_section_key(p_section, "display_mode")) {
DisplayMode dock_filesystem_display_mode = DisplayMode(int(p_layout->get_value(p_section, "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")));
if (p_layout->has_section_key(p_section, "file_sort")) {
FileSortOption dock_filesystem_file_sort = FileSortOption(int(p_layout->get_value(p_section, "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")));
if (p_layout->has_section_key(p_section, "file_list_display_mode")) {
FileListDisplayMode dock_filesystem_file_list_display_mode = FileListDisplayMode(int(p_layout->get_value(p_section, "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");
if (p_layout->has_section_key(p_section, "selected_paths")) {
PackedStringArray dock_filesystem_selected_paths = p_layout->get_value(p_section, "selected_paths");
for (int i = 0; i < dock_filesystem_selected_paths.size(); i++) {
select_file(dock_filesystem_selected_paths[i]);
}
@ -4117,8 +4090,8 @@ void FileSystemDock::_load_layout_from_config(Ref<ConfigFile> p_layout, const St
// 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");
if (p_layout->has_section_key(p_section, "uncollapsed_paths")) {
uncollapsed_tis = p_layout->get_value(p_section, "uncollapsed_paths");
} else {
uncollapsed_tis = { "res://" };
}
@ -4134,10 +4107,32 @@ void FileSystemDock::_load_layout_from_config(Ref<ConfigFile> p_layout, const St
}
}
void FileSystemDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("navigate_to_path", "path"), &FileSystemDock::navigate_to_path);
ClassDB::bind_method(D_METHOD("add_resource_tooltip_plugin", "plugin"), &FileSystemDock::add_resource_tooltip_plugin);
ClassDB::bind_method(D_METHOD("remove_resource_tooltip_plugin", "plugin"), &FileSystemDock::remove_resource_tooltip_plugin);
ADD_SIGNAL(MethodInfo("inherit", PropertyInfo(Variant::STRING, "file")));
ADD_SIGNAL(MethodInfo("instantiate", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files")));
ADD_SIGNAL(MethodInfo("resource_removed", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
ADD_SIGNAL(MethodInfo("file_removed", PropertyInfo(Variant::STRING, "file")));
ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder")));
ADD_SIGNAL(MethodInfo("files_moved", PropertyInfo(Variant::STRING, "old_file"), PropertyInfo(Variant::STRING, "new_file")));
ADD_SIGNAL(MethodInfo("folder_moved", PropertyInfo(Variant::STRING, "old_folder"), PropertyInfo(Variant::STRING, "new_folder")));
ADD_SIGNAL(MethodInfo("folder_color_changed"));
ADD_SIGNAL(MethodInfo("display_mode_changed"));
}
FileSystemDock::FileSystemDock() {
singleton = this;
set_name("FileSystem");
current_path = "res://";
set_name(TTRC("FileSystem"));
set_icon_name("Folder");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_filesystem", TTRC("Open FileSystem Dock"), KeyModifierMask::ALT | Key::F));
set_default_slot(EditorDockManager::DOCK_SLOT_LEFT_BR);
set_available_layouts(DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_HORIZONTAL);
ProjectSettings::get_singleton()->add_hidden_prefix("file_customization/");
@ -4171,8 +4166,11 @@ FileSystemDock::FileSystemDock() {
editor_is_dark_theme = EditorThemeManager::is_dark_theme();
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
VBoxContainer *top_vbc = memnew(VBoxContainer);
add_child(top_vbc);
main_vb->add_child(top_vbc);
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
top_vbc->add_child(toolbar_hbc);
@ -4241,7 +4239,7 @@ FileSystemDock::FileSystemDock() {
split_box->set_v_size_flags(SIZE_EXPAND_FILL);
split_box->connect("dragged", callable_mp(this, &FileSystemDock::_split_dragged));
split_box_offset_h = 240 * EDSCALE;
add_child(split_box);
main_vb->add_child(split_box);
tree = memnew(FileSystemTree);
tree->set_accessibility_name(TTRC("Directories"));
@ -4305,7 +4303,7 @@ FileSystemDock::FileSystemDock() {
scanning_vb = memnew(VBoxContainer);
scanning_vb->hide();
add_child(scanning_vb);
main_vb->add_child(scanning_vb);
Label *slabel = memnew(Label);
slabel->set_text(TTRC("Scanning Files,\nPlease Wait..."));

View file

@ -30,6 +30,7 @@
#pragma once
#include "editor/docks/editor_dock.h"
#include "editor/file_system/dependency_editor.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/file_system/file_info.h"
@ -44,6 +45,7 @@
class CreateDialog;
class EditorDirDialog;
class HBoxContainer;
class ItemList;
class LineEdit;
class ProgressBar;
@ -51,6 +53,7 @@ class SceneCreateDialog;
class ShaderCreateDialog;
class DirectoryCreateDialog;
class EditorResourceTooltipPlugin;
class VBoxContainer;
class FileSystemTree : public Tree {
virtual Control *make_custom_tooltip(const String &p_text) const;
@ -78,8 +81,8 @@ public:
FileSystemList();
};
class FileSystemDock : public VBoxContainer {
GDCLASS(FileSystemDock, VBoxContainer);
class FileSystemDock : public EditorDock {
GDCLASS(FileSystemDock, EditorDock);
public:
enum FileListDisplayMode {
@ -230,7 +233,7 @@ private:
int history_pos;
int history_max_size;
String current_path;
String current_path = "res://";
String select_after_scan;
bool updating_tree = false;
@ -361,12 +364,6 @@ private:
void _change_bottom_dock_placement();
bool _can_dock_horizontal() const;
void _set_dock_horizontal(bool p_enable);
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);
private:
inline static FileSystemDock *singleton = nullptr;
@ -377,6 +374,10 @@ protected:
void _notification(int p_what);
static void _bind_methods();
virtual void update_layout(EditorDock::DockLayout p_layout) override;
virtual void save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const override;
virtual void load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) override;
public:
static constexpr double ITEM_COLOR_SCALE = 1.75;
static constexpr double ITEM_ALPHA_MIN = 0.1;

View file

@ -34,6 +34,7 @@
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_settings.h"
#include "scene/gui/check_box.h"
#include "scene/gui/item_list.h"
@ -175,14 +176,14 @@ void HistoryDock::refresh_version() {
action_list->set_current(idx);
}
void HistoryDock::_save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
p_layout->set_value(p_section, "dock_history_include_scene", current_scene_checkbox->is_pressed());
p_layout->set_value(p_section, "dock_history_include_global", global_history_checkbox->is_pressed());
void HistoryDock::save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const {
p_layout->set_value(p_section, "include_scene", current_scene_checkbox->is_pressed());
p_layout->set_value(p_section, "include_global", global_history_checkbox->is_pressed());
}
void HistoryDock::_load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
current_scene_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "dock_history_include_scene", true));
global_history_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "dock_history_include_global", true));
void HistoryDock::load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) {
current_scene_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "include_scene", true));
global_history_checkbox->set_pressed_no_signal(p_layout->get_value(p_section, "include_global", true));
refresh_history();
}
@ -234,20 +235,21 @@ void HistoryDock::_notification(int p_notification) {
}
}
void HistoryDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_save_layout_to_config"), &HistoryDock::_save_layout_to_config);
ClassDB::bind_method(D_METHOD("_load_layout_from_config"), &HistoryDock::_load_layout_from_config);
}
HistoryDock::HistoryDock() {
set_name("History");
set_name(TTRC("History"));
set_icon_name("History");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_history", TTRC("Open History Dock")));
set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL);
ur_manager = EditorUndoRedoManager::get_singleton();
ur_manager->connect("history_changed", callable_mp(this, &HistoryDock::on_history_changed));
ur_manager->connect("version_changed", callable_mp(this, &HistoryDock::on_version_changed));
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
HBoxContainer *mode_hb = memnew(HBoxContainer);
add_child(mode_hb);
main_vb->add_child(mode_hb);
current_scene_checkbox = memnew(CheckBox);
mode_hb->add_child(current_scene_checkbox);
@ -269,7 +271,7 @@ HistoryDock::HistoryDock() {
action_list = memnew(ItemList);
action_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(action_list);
main_vb->add_child(action_list);
action_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
action_list->connect(SceneStringName(item_selected), callable_mp(this, &HistoryDock::seek_history));
}

View file

@ -30,15 +30,15 @@
#pragma once
#include "scene/gui/box_container.h"
#include "editor/docks/editor_dock.h"
class CheckBox;
class ConfigFile;
class ItemList;
class EditorUndoRedoManager;
class HistoryDock : public VBoxContainer {
GDCLASS(HistoryDock, VBoxContainer);
class HistoryDock : public EditorDock {
GDCLASS(HistoryDock, EditorDock);
EditorUndoRedoManager *ur_manager;
ItemList *action_list = nullptr;
@ -54,12 +54,11 @@ class HistoryDock : public VBoxContainer {
void on_version_changed();
void refresh_version();
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);
protected:
void _notification(int p_notification);
static void _bind_methods();
virtual void save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const override;
virtual void load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) override;
public:
void seek_history(int p_index);

View file

@ -35,9 +35,11 @@
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector/editor_resource_preview.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/box_container.h"
class ImportDockParameters : public Object {
GDCLASS(ImportDockParameters, Object);
@ -742,11 +744,17 @@ void ImportDock::initialize_import_options() const {
ImportDock::ImportDock() {
singleton = this;
set_name("Import");
set_name(TTRC("Import"));
set_icon_name("FileAccess");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_import", TTRC("Open Import Dock")));
set_default_slot(EditorDockManager::DOCK_SLOT_LEFT_UR);
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
content = memnew(VBoxContainer);
content->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(content);
main_vb->add_child(content);
content->hide();
imported = memnew(Label);
@ -819,7 +827,7 @@ ImportDock::ImportDock() {
select_a_resource->set_v_size_flags(SIZE_EXPAND_FILL);
select_a_resource->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
select_a_resource->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
add_child(select_a_resource);
main_vb->add_child(select_a_resource);
}
ImportDock::~ImportDock() {

View file

@ -32,6 +32,7 @@
#include "core/io/config_file.h"
#include "core/io/resource_importer.h"
#include "editor/docks/editor_dock.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/inspector/editor_inspector.h"
#include "scene/gui/box_container.h"
@ -41,8 +42,10 @@
#include "scene/gui/popup_menu.h"
class ImportDockParameters;
class ImportDock : public VBoxContainer {
GDCLASS(ImportDock, VBoxContainer);
class VBoxContainer;
class ImportDock : public EditorDock {
GDCLASS(ImportDock, EditorDock);
Label *imported = nullptr;
OptionButton *import_as = nullptr;

View file

@ -40,10 +40,10 @@
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_object_selector.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
InspectorDock *InspectorDock::singleton = nullptr;
#include "scene/gui/box_container.h"
void InspectorDock::_prepare_menu() {
PopupMenu *menu = object_menu->get_popup();
@ -707,14 +707,20 @@ void InspectorDock::shortcut_input(const Ref<InputEvent> &p_event) {
InspectorDock::InspectorDock(EditorData &p_editor_data) {
singleton = this;
set_name("Inspector");
set_name(TTRC("Inspector"));
set_icon_name("AnimationTrackList");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_inspector", TTRC("Open Inspector Dock")));
set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL);
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
editor_data = &p_editor_data;
property_name_style = EditorPropertyNameProcessor::get_default_inspector_style();
HBoxContainer *general_options_hb = memnew(HBoxContainer);
add_child(general_options_hb);
main_vb->add_child(general_options_hb);
resource_new_button = memnew(Button);
resource_new_button->set_theme_type_variation("FlatMenuButton");
@ -782,7 +788,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
history_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &InspectorDock::_select_history));
HBoxContainer *subresource_hb = memnew(HBoxContainer);
add_child(subresource_hb);
main_vb->add_child(subresource_hb);
object_selector = memnew(EditorObjectSelector(EditorNode::get_singleton()->get_editor_selection_history()));
object_selector->set_h_size_flags(Control::SIZE_EXPAND_FILL);
subresource_hb->add_child(object_selector);
@ -801,7 +807,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
new_resource_dialog->connect("create", callable_mp(this, &InspectorDock::_resource_created));
HBoxContainer *property_tools_hb = memnew(HBoxContainer);
add_child(property_tools_hb);
main_vb->add_child(property_tools_hb);
search = memnew(LineEdit);
search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@ -818,14 +824,14 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
object_menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &InspectorDock::_menu_option));
info = memnew(Button);
add_child(info);
main_vb->add_child(info);
info->set_clip_text(true);
info->set_accessibility_name(TTRC("Information"));
info->hide();
info->connect(SceneStringName(pressed), callable_mp(this, &InspectorDock::_info_pressed));
unique_resources_confirmation = memnew(ConfirmationDialog);
add_child(unique_resources_confirmation);
main_vb->add_child(unique_resources_confirmation);
VBoxContainer *container = memnew(VBoxContainer);
unique_resources_confirmation->add_child(container);
@ -852,12 +858,12 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
EditorNode::get_singleton()->get_gui_base()->add_child(info_dialog);
load_resource_dialog = memnew(EditorFileDialog);
add_child(load_resource_dialog);
main_vb->add_child(load_resource_dialog);
load_resource_dialog->set_current_dir("res://");
load_resource_dialog->connect("file_selected", callable_mp(this, &InspectorDock::_resource_file_selected));
inspector = memnew(EditorInspector);
add_child(inspector);
main_vb->add_child(inspector);
inspector->set_autoclear(true);
inspector->set_show_categories(true, true);
inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);

View file

@ -30,6 +30,7 @@
#pragma once
#include "editor/docks/editor_dock.h"
#include "editor/editor_data.h"
#include "editor/gui/create_dialog.h"
#include "editor/inspector/editor_inspector.h"
@ -43,8 +44,8 @@
class EditorFileDialog;
class EditorObjectSelector;
class InspectorDock : public VBoxContainer {
GDCLASS(InspectorDock, VBoxContainer);
class InspectorDock : public EditorDock {
GDCLASS(InspectorDock, EditorDock);
enum MenuOptions {
RESOURCE_LOAD,
@ -136,7 +137,7 @@ class InspectorDock : public VBoxContainer {
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
private:
static InspectorDock *singleton;
static inline InspectorDock *singleton = nullptr;
public:
static InspectorDock *get_singleton() { return singleton; }

View file

@ -32,6 +32,7 @@
#include "core/io/config_file.h"
#include "editor/scene/connections_dialog.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/themes/editor_scale.h"
void NodeDock::show_groups() {
@ -48,12 +49,12 @@ void NodeDock::show_connections() {
connections->show();
}
void NodeDock::_save_layout_to_config(Ref<ConfigFile> p_layout, const String &p_section) const {
p_layout->set_value(p_section, "dock_node_current_tab", int(groups_button->is_pressed()));
void NodeDock::save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const {
p_layout->set_value(p_section, "current_tab", int(groups_button->is_pressed()));
}
void NodeDock::_load_layout_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
const int current_tab = p_layout->get_value(p_section, "dock_node_current_tab", 0);
void NodeDock::load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) {
const int current_tab = p_layout->get_value(p_section, "current_tab", 0);
if (select_a_node->is_visible()) {
if (current_tab == 0) {
groups_button->set_pressed_no_signal(false);
@ -78,11 +79,6 @@ void NodeDock::_notification(int p_what) {
}
}
void NodeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_save_layout_to_config"), &NodeDock::_save_layout_to_config);
ClassDB::bind_method(D_METHOD("_load_layout_from_config"), &NodeDock::_load_layout_from_config);
}
void NodeDock::update_lists() {
connections->update_tree();
}
@ -110,10 +106,16 @@ void NodeDock::set_node(Node *p_node) {
NodeDock::NodeDock() {
singleton = this;
set_name(TTRC("Node"));
set_icon_name("Object");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_node", TTRC("Open Node Dock")));
set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL);
VBoxContainer *main_vb = memnew(VBoxContainer);
add_child(main_vb);
set_name("Node");
mode_hb = memnew(HBoxContainer);
add_child(mode_hb);
main_vb->add_child(mode_hb);
mode_hb->hide();
connections_button = memnew(Button);
@ -137,12 +139,12 @@ NodeDock::NodeDock() {
groups_button->connect(SceneStringName(pressed), callable_mp(this, &NodeDock::show_groups));
connections = memnew(ConnectionsDock);
add_child(connections);
main_vb->add_child(connections);
connections->set_v_size_flags(SIZE_EXPAND_FILL);
connections->hide();
groups = memnew(GroupsEditor);
add_child(groups);
main_vb->add_child(groups);
groups->set_v_size_flags(SIZE_EXPAND_FILL);
groups->hide();
@ -154,7 +156,7 @@ NodeDock::NodeDock() {
select_a_node->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
select_a_node->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
select_a_node->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
add_child(select_a_node);
main_vb->add_child(select_a_node);
}
NodeDock::~NodeDock() {

View file

@ -30,13 +30,14 @@
#pragma once
#include "editor/docks/editor_dock.h"
#include "groups_editor.h"
class ConfigFile;
class ConnectionsDock;
class NodeDock : public VBoxContainer {
GDCLASS(NodeDock, VBoxContainer);
class NodeDock : public EditorDock {
GDCLASS(NodeDock, EditorDock);
Button *connections_button = nullptr;
Button *groups_button = nullptr;
@ -48,9 +49,6 @@ class NodeDock : public VBoxContainer {
Label *select_a_node = nullptr;
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);
private:
inline static NodeDock *singleton = nullptr;
@ -59,7 +57,9 @@ public:
protected:
void _notification(int p_what);
static void _bind_methods();
virtual void save_layout_to_config(Ref<ConfigFile> &p_layout, const String &p_section) const override;
virtual void load_layout_from_config(const Ref<ConfigFile> &p_layout, const String &p_section) override;
public:
void set_node(Node *p_node);

View file

@ -55,6 +55,7 @@
#include "editor/scene/rename_dialog.h"
#include "editor/scene/reparent_dialog.h"
#include "editor/script/script_editor_plugin.h"
#include "editor/settings/editor_command_palette.h"
#include "editor/settings/editor_feature_profile.h"
#include "editor/settings/editor_settings.h"
#include "editor/shader/shader_create_dialog.h"
@ -62,6 +63,7 @@
#include "scene/2d/node_2d.h"
#include "scene/animation/animation_tree.h"
#include "scene/audio/audio_stream_player.h"
#include "scene/gui/box_container.h"
#include "scene/gui/check_box.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h"
@ -4689,13 +4691,18 @@ void SceneTreeDock::_update_configuration_warning() {
}
SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data) {
set_name(TTRC("Scene"));
set_icon_name("PackedScene");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_scene", TTRC("Open Scene Dock")));
set_default_slot(EditorDockManager::DOCK_SLOT_LEFT_UR);
singleton = this;
set_name("Scene");
editor_data = &p_editor_data;
editor_selection = p_editor_selection;
scene_root = p_scene_root;
VBoxContainer *vbc = this;
VBoxContainer *vbc = memnew(VBoxContainer);
add_child(vbc);
HBoxContainer *filter_hbc = memnew(HBoxContainer);
filter_hbc->add_theme_constant_override("separate", 0);

View file

@ -30,22 +30,24 @@
#pragma once
#include "editor/docks/editor_dock.h"
#include "editor/scene/scene_tree_editor.h"
#include "editor/script/script_create_dialog.h"
#include "scene/gui/box_container.h"
#include "scene/resources/animation.h"
class CheckBox;
class EditorData;
class EditorSelection;
class HBoxContainer;
class MenuButton;
class RenameDialog;
class ReparentDialog;
class ShaderCreateDialog;
class TextureRect;
class VBoxContainer;
class SceneTreeDock : public VBoxContainer {
GDCLASS(SceneTreeDock, VBoxContainer);
class SceneTreeDock : public EditorDock {
GDCLASS(SceneTreeDock, EditorDock);
enum Tool {
TOOL_NEW,

View file

@ -6070,9 +6070,14 @@ void EditorNode::_update_layouts_menu() {
}
Vector<String> layouts = config->get_sections();
const String default_layout_name = TTR("Default");
for (const String &layout : layouts) {
if (layout == TTR("Default")) {
if (layout.contains_char('/')) {
continue;
}
if (layout == default_layout_name) {
editor_layouts->remove_item(editor_layouts->get_item_index(LAYOUT_DEFAULT));
overridden_default_layout = editor_layouts->get_item_count();
}
@ -8383,36 +8388,26 @@ EditorNode::EditorNode() {
// Instantiate and place editor docks.
memnew(SceneTreeDock(scene_root, editor_selection, editor_data));
memnew(FileSystemDock);
memnew(InspectorDock(editor_data));
memnew(ImportDock);
memnew(NodeDock);
editor_dock_manager->add_dock(SceneTreeDock::get_singleton());
FileSystemDock *filesystem_dock = FileSystemDock::get_singleton();
memnew(ImportDock);
editor_dock_manager->add_dock(ImportDock::get_singleton());
FileSystemDock *filesystem_dock = memnew(FileSystemDock);
filesystem_dock->connect("inherit", callable_mp(this, &EditorNode::_inherit_request));
filesystem_dock->connect("instantiate", callable_mp(this, &EditorNode::_instantiate_request));
filesystem_dock->connect("display_mode_changed", callable_mp(this, &EditorNode::_save_editor_layout));
get_project_settings()->connect_filesystem_dock_signals(filesystem_dock);
editor_dock_manager->add_dock(filesystem_dock);
memnew(InspectorDock(editor_data));
editor_dock_manager->add_dock(InspectorDock::get_singleton());
memnew(NodeDock);
editor_dock_manager->add_dock(NodeDock::get_singleton());
history_dock = memnew(HistoryDock);
// Scene: Top left.
editor_dock_manager->add_dock(SceneTreeDock::get_singleton(), TTRC("Scene"), EditorDockManager::DOCK_SLOT_LEFT_UR, ED_SHORTCUT_AND_COMMAND("docks/open_scene", TTRC("Open Scene Dock")), "PackedScene");
// Import: Top left, behind Scene.
editor_dock_manager->add_dock(ImportDock::get_singleton(), TTRC("Import"), EditorDockManager::DOCK_SLOT_LEFT_UR, ED_SHORTCUT_AND_COMMAND("docks/open_import", TTRC("Open Import Dock")), "FileAccess");
// FileSystem: Bottom left.
editor_dock_manager->add_dock(FileSystemDock::get_singleton(), TTRC("FileSystem"), EditorDockManager::DOCK_SLOT_LEFT_BR, ED_SHORTCUT_AND_COMMAND("docks/open_filesystem", TTRC("Open FileSystem Dock"), KeyModifierMask::ALT | Key::F), "Folder");
// Inspector: Full height right.
editor_dock_manager->add_dock(InspectorDock::get_singleton(), TTRC("Inspector"), EditorDockManager::DOCK_SLOT_RIGHT_UL, ED_SHORTCUT_AND_COMMAND("docks/open_inspector", TTRC("Open Inspector Dock")), "AnimationTrackList");
// Node: Full height right, behind Inspector.
editor_dock_manager->add_dock(NodeDock::get_singleton(), TTRC("Node"), EditorDockManager::DOCK_SLOT_RIGHT_UL, ED_SHORTCUT_AND_COMMAND("docks/open_node", TTRC("Open Node Dock")), "Object");
// History: Full height right, behind Node.
editor_dock_manager->add_dock(history_dock, TTRC("History"), EditorDockManager::DOCK_SLOT_RIGHT_UL, ED_SHORTCUT_AND_COMMAND("docks/open_history", TTRC("Open History Dock")), "History");
editor_dock_manager->add_dock(history_dock);
// Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize.
left_r_hsplit->set_split_offset(270 * EDSCALE);

View file

@ -33,6 +33,7 @@
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/editor_debugger_plugin.h"
#include "editor/docks/editor_dock.h"
#include "editor/docks/editor_dock_manager.h"
#include "editor/docks/inspector_dock.h"
#include "editor/docks/scene_tree_dock.h"
@ -84,24 +85,48 @@ Button *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const Stri
return EditorNode::get_bottom_panel()->add_item(p_title, p_control, p_shortcut);
}
#ifndef DISABLE_DEPRECATED
void EditorPlugin::add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut) {
ERR_FAIL_NULL(p_control);
EditorDockManager::get_singleton()->add_dock(p_control, String(), EditorDockManager::DockSlot(p_slot), p_shortcut);
ERR_FAIL_COND(legacy_docks.has(p_control));
EditorDock *dock = memnew(EditorDock);
dock->set_title(p_control->get_name());
dock->set_dock_shortcut(p_shortcut);
dock->set_default_slot((EditorDockManager::DockSlot)p_slot);
dock->add_child(p_control);
legacy_docks[p_control] = dock;
EditorDockManager::get_singleton()->add_dock(dock);
}
void EditorPlugin::remove_control_from_docks(Control *p_control) {
ERR_FAIL_NULL(p_control);
EditorDockManager::get_singleton()->remove_dock(p_control);
ERR_FAIL_COND(!legacy_docks.has(p_control));
EditorDockManager::get_singleton()->remove_dock(legacy_docks[p_control]);
legacy_docks[p_control]->queue_free();
legacy_docks.erase(p_control);
}
void EditorPlugin::set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon) {
ERR_FAIL_NULL(p_control);
ERR_FAIL_COND(!legacy_docks.has(p_control));
legacy_docks[p_control]->set_dock_icon(p_icon);
}
#endif
void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
ERR_FAIL_NULL(p_control);
EditorNode::get_bottom_panel()->remove_item(p_control);
}
void EditorPlugin::set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon) {
ERR_FAIL_NULL(p_control);
EditorDockManager::get_singleton()->set_dock_tab_icon(p_control, p_icon);
void EditorPlugin::add_dock(EditorDock *p_dock) {
EditorDockManager::get_singleton()->add_dock(p_dock);
}
void EditorPlugin::remove_dock(EditorDock *p_dock) {
EditorDockManager::get_singleton()->remove_dock(p_dock);
}
void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
@ -588,13 +613,12 @@ void EditorPlugin::_notification(int p_what) {
}
void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_dock", "dock"), &EditorPlugin::add_dock);
ClassDB::bind_method(D_METHOD("remove_dock", "dock"), &EditorPlugin::remove_dock);
ClassDB::bind_method(D_METHOD("add_control_to_container", "container", "control"), &EditorPlugin::add_control_to_container);
ClassDB::bind_method(D_METHOD("add_control_to_bottom_panel", "control", "title", "shortcut"), &EditorPlugin::add_control_to_bottom_panel, DEFVAL(Ref<Shortcut>()));
ClassDB::bind_method(D_METHOD("add_control_to_dock", "slot", "control", "shortcut"), &EditorPlugin::add_control_to_dock, DEFVAL(Ref<Shortcut>()));
ClassDB::bind_method(D_METHOD("remove_control_from_docks", "control"), &EditorPlugin::remove_control_from_docks);
ClassDB::bind_method(D_METHOD("remove_control_from_bottom_panel", "control"), &EditorPlugin::remove_control_from_bottom_panel);
ClassDB::bind_method(D_METHOD("remove_control_from_container", "container", "control"), &EditorPlugin::remove_control_from_container);
ClassDB::bind_method(D_METHOD("set_dock_tab_icon", "control", "icon"), &EditorPlugin::set_dock_tab_icon);
ClassDB::bind_method(D_METHOD("add_tool_menu_item", "name", "callable"), &EditorPlugin::add_tool_menu_item);
ClassDB::bind_method(D_METHOD("add_tool_submenu_item", "name", "submenu"), &EditorPlugin::add_tool_submenu_item);
ClassDB::bind_method(D_METHOD("remove_tool_menu_item", "name"), &EditorPlugin::remove_tool_menu_item);
@ -602,6 +626,12 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_custom_type", "type", "base", "script", "icon"), &EditorPlugin::add_custom_type);
ClassDB::bind_method(D_METHOD("remove_custom_type", "type"), &EditorPlugin::remove_custom_type);
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("add_control_to_dock", "slot", "control", "shortcut"), &EditorPlugin::add_control_to_dock, DEFVAL(Ref<Shortcut>()));
ClassDB::bind_method(D_METHOD("remove_control_from_docks", "control"), &EditorPlugin::remove_control_from_docks);
ClassDB::bind_method(D_METHOD("set_dock_tab_icon", "control", "icon"), &EditorPlugin::set_dock_tab_icon);
#endif
ClassDB::bind_method(D_METHOD("add_autoload_singleton", "name", "path"), &EditorPlugin::add_autoload_singleton);
ClassDB::bind_method(D_METHOD("remove_autoload_singleton", "name"), &EditorPlugin::remove_autoload_singleton);
@ -688,6 +718,7 @@ void EditorPlugin::_bind_methods() {
BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_LEFT);
BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_RIGHT);
BIND_ENUM_CONSTANT(DOCK_SLOT_NONE);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_UL);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_BL);
BIND_ENUM_CONSTANT(DOCK_SLOT_LEFT_UR);

View file

@ -31,6 +31,7 @@
#pragma once
#include "core/io/config_file.h"
#include "editor/docks/editor_dock_manager.h"
#include "editor/inspector/editor_context_menu_plugin.h"
#include "scene/3d/camera_3d.h"
#include "scene/gui/control.h"
@ -39,6 +40,7 @@ class Node3D;
class Button;
class PopupMenu;
class EditorDebuggerPlugin;
class EditorDock;
class EditorExport;
class EditorExportPlugin;
class EditorExportPlatform;
@ -65,6 +67,8 @@ class EditorPlugin : public Node {
String plugin_version;
#ifndef DISABLE_DEPRECATED
static inline HashMap<Control *, EditorDock *> legacy_docks;
void _editor_project_settings_changed();
#endif
@ -85,15 +89,16 @@ 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
DOCK_SLOT_NONE = EditorDockManager::DOCK_SLOT_NONE,
DOCK_SLOT_LEFT_UL = EditorDockManager::DOCK_SLOT_LEFT_UL,
DOCK_SLOT_LEFT_BL = EditorDockManager::DOCK_SLOT_LEFT_BL,
DOCK_SLOT_LEFT_UR = EditorDockManager::DOCK_SLOT_LEFT_UR,
DOCK_SLOT_LEFT_BR = EditorDockManager::DOCK_SLOT_LEFT_BR,
DOCK_SLOT_RIGHT_UL = EditorDockManager::DOCK_SLOT_RIGHT_UL,
DOCK_SLOT_RIGHT_BL = EditorDockManager::DOCK_SLOT_RIGHT_BL,
DOCK_SLOT_RIGHT_UR = EditorDockManager::DOCK_SLOT_RIGHT_UR,
DOCK_SLOT_RIGHT_BR = EditorDockManager::DOCK_SLOT_RIGHT_BR,
DOCK_SLOT_MAX = EditorDockManager::DOCK_SLOT_MAX
};
enum AfterGUIInput {
@ -140,6 +145,10 @@ protected:
Button *_add_control_to_bottom_panel_compat_88081(Control *p_control, const String &p_title);
void _add_control_to_dock_compat_88081(DockSlot p_slot, Control *p_control);
static void _bind_compatibility_methods();
void add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut = nullptr);
void remove_control_from_docks(Control *p_control);
void set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon);
#endif
public:
@ -148,11 +157,10 @@ public:
void add_control_to_container(CustomControlContainer p_location, Control *p_control);
void remove_control_from_container(CustomControlContainer p_location, Control *p_control);
Button *add_control_to_bottom_panel(Control *p_control, const String &p_title, const Ref<Shortcut> &p_shortcut = nullptr);
void add_control_to_dock(DockSlot p_slot, Control *p_control, const Ref<Shortcut> &p_shortcut = nullptr);
void remove_control_from_docks(Control *p_control);
void remove_control_from_bottom_panel(Control *p_control);
void set_dock_tab_icon(Control *p_control, const Ref<Texture2D> &p_icon);
void add_dock(EditorDock *p_dock);
void remove_dock(EditorDock *p_dock);
void add_tool_menu_item(const String &p_name, const Callable &p_callable);
void add_tool_submenu_item(const String &p_name, PopupMenu *p_submenu);

View file

@ -146,6 +146,7 @@ void register_editor_types() {
GDREGISTER_CLASS(EditorTranslationParserPlugin);
GDREGISTER_CLASS(EditorImportPlugin);
GDREGISTER_CLASS(EditorScript);
GDREGISTER_CLASS(EditorDock);
GDREGISTER_CLASS(EditorSelection);
GDREGISTER_CLASS(EditorFileDialog);
GDREGISTER_CLASS(EditorSettings);

View file

@ -98,7 +98,9 @@ void EditorLayoutsDialog::_post_popup() {
Vector<String> layouts = config->get_sections();
for (const String &E : layouts) {
layout_names->add_item(E);
if (!E.contains_char('/')) {
layout_names->add_item(E);
}
}
if (name->is_visible()) {
name->grab_focus();

View file

@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
#include "core/os/time.h"
#include "editor/docks/editor_dock.h"
#include "editor/docks/editor_dock_manager.h"
#include "editor/docks/filesystem_dock.h"
#include "editor/editor_interface.h"
@ -907,7 +908,7 @@ void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() {
}
void VersionControlEditorPlugin::register_editor() {
EditorDockManager::get_singleton()->add_dock(version_commit_dock, "", EditorDockManager::DOCK_SLOT_RIGHT_UL, ED_SHORTCUT_AND_COMMAND("docks/open_version_control", TTRC("Open Version Control Dock")));
EditorDockManager::get_singleton()->add_dock(version_commit_dock);
version_control_dock_button = EditorNode::get_bottom_panel()->add_item(TTRC("Version Control"), version_control_dock, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_version_control_bottom_panel", TTRC("Toggle Version Control Bottom Panel")));
@ -1144,14 +1145,21 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
set_up_warning_text->set_h_size_flags(Control::SIZE_EXPAND_FILL);
set_up_settings_vbc->add_child(set_up_warning_text);
version_commit_dock = memnew(VBoxContainer);
version_commit_dock = memnew(EditorDock);
version_commit_dock->set_visible(false);
version_commit_dock->set_name(TTR("Commit"));
version_commit_dock->set_name(TTRC("Commit"));
version_commit_dock->set_layout_key("VersionCommit");
version_commit_dock->set_icon_name("VcsBranches");
version_commit_dock->set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_version_control", TTRC("Open Version Control Dock")));
version_commit_dock->set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL);
VBoxContainer *dock_vb = memnew(VBoxContainer);
version_commit_dock->add_child(dock_vb);
VBoxContainer *unstage_area = memnew(VBoxContainer);
unstage_area->set_v_size_flags(Control::SIZE_EXPAND_FILL);
unstage_area->set_h_size_flags(Control::SIZE_EXPAND_FILL);
version_commit_dock->add_child(unstage_area);
dock_vb->add_child(unstage_area);
HBoxContainer *unstage_title = memnew(HBoxContainer);
unstage_area->add_child(unstage_title);
@ -1178,7 +1186,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
discard_all_confirm->set_hide_on_ok(true);
discard_all_confirm->set_ok_button_text(TTR("Permanentally delete my changes"));
discard_all_confirm->add_cancel_button();
version_commit_dock->add_child(discard_all_confirm);
dock_vb->add_child(discard_all_confirm);
discard_all_confirm->get_ok_button()->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_discard_all));
@ -1210,7 +1218,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
VBoxContainer *stage_area = memnew(VBoxContainer);
stage_area->set_v_size_flags(Control::SIZE_EXPAND_FILL);
stage_area->set_h_size_flags(Control::SIZE_EXPAND_FILL);
version_commit_dock->add_child(stage_area);
dock_vb->add_child(stage_area);
HBoxContainer *stage_title = memnew(HBoxContainer);
stage_area->add_child(stage_title);
@ -1242,10 +1250,10 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
unstage_all_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_move_all).bind(staged_files));
stage_all_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_move_all).bind(unstaged_files));
version_commit_dock->add_child(memnew(HSeparator));
dock_vb->add_child(memnew(HSeparator));
VBoxContainer *commit_area = memnew(VBoxContainer);
version_commit_dock->add_child(commit_area);
dock_vb->add_child(commit_area);
Label *commit_label = memnew(Label);
commit_label->set_text(TTR("Commit Message"));
@ -1271,10 +1279,10 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
commit_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_commit));
commit_area->add_child(commit_button);
version_commit_dock->add_child(memnew(HSeparator));
dock_vb->add_child(memnew(HSeparator));
HBoxContainer *commit_list_hbc = memnew(HBoxContainer);
version_commit_dock->add_child(commit_list_hbc);
dock_vb->add_child(commit_list_hbc);
Label *commit_list_label = memnew(Label);
commit_list_label->set_text(TTR("Commit List"));
@ -1304,14 +1312,14 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
commit_list->set_column_custom_minimum_width(1, 20);
commit_list->set_theme_type_variation("TreeSecondary");
commit_list->connect(SceneStringName(item_selected), callable_mp(this, &VersionControlEditorPlugin::_load_diff).bind(commit_list));
version_commit_dock->add_child(commit_list);
dock_vb->add_child(commit_list);
version_commit_dock->add_child(memnew(HSeparator));
dock_vb->add_child(memnew(HSeparator));
HFlowContainer *menu_bar = memnew(HFlowContainer);
menu_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
menu_bar->set_v_size_flags(Control::SIZE_FILL);
version_commit_dock->add_child(menu_bar);
dock_vb->add_child(menu_bar);
branch_select = memnew(OptionButton);
branch_select->set_tooltip_text(TTR("Branches"));

View file

@ -39,6 +39,8 @@
#include "scene/gui/text_edit.h"
#include "scene/gui/tree.h"
class EditorDock;
class VersionControlEditorPlugin : public EditorPlugin {
GDCLASS(VersionControlEditorPlugin, EditorPlugin)
@ -98,7 +100,7 @@ private:
HashMap<EditorVCSInterface::ChangeType, Color> change_type_to_color;
HashMap<EditorVCSInterface::ChangeType, Ref<Texture>> change_type_to_icon;
VBoxContainer *version_commit_dock = nullptr;
EditorDock *version_commit_dock = nullptr;
Tree *staged_files = nullptr;
Tree *unstaged_files = nullptr;
Tree *commit_list = nullptr;