├── .gitignore
├── .gitattributes
├── .gdignore
├── screenshots.png
└── screenshots.png.import
├── addons
└── dock_slot_expander
│ ├── plugin.cfg
│ ├── icon.svg.import
│ ├── LICENSE
│ ├── icon.svg
│ └── dock_slot_expander.gd
├── README.md
├── project.godot
├── icon.svg
├── icon.svg.import
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | # Godot 4+ specific ignores
2 | .godot/
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Normalize EOL for all files that Git considers text files.
2 | * text=auto eol=lf
3 |
--------------------------------------------------------------------------------
/.gdignore/screenshots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bypell/dock_slot_expander/HEAD/.gdignore/screenshots.png
--------------------------------------------------------------------------------
/addons/dock_slot_expander/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="DockSlotExpander"
4 | description="Editor plugin that allows you to expand any editor dock slot by hovering over it and pressing ctrl+space. Especially useful to view the scene dock or the file system dock better."
5 | author="bypell"
6 | version="1.0"
7 | script="dock_slot_expander.gd"
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dock Slot Expander
2 | Editor plugin that allows you to expand any editor dock slot by hovering over it and pressing ctrl+space or clicking the middle mouse button.\
3 | It makes browsing large scene trees/file structures easier.
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=5
10 |
11 | [application]
12 |
13 | config/name="DockSlotExpander"
14 | config/features=PackedStringArray("4.2", "GL Compatibility")
15 | config/icon="res://icon.svg"
16 |
17 | [editor_plugins]
18 |
19 | enabled=PackedStringArray("res://addons/dock_slot_expander/plugin.cfg")
20 |
21 | [rendering]
22 |
23 | renderer/rendering_method="gl_compatibility"
24 | renderer/rendering_method.mobile="gl_compatibility"
25 |
--------------------------------------------------------------------------------
/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.gdignore/screenshots.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dsygdd5qxs4kg"
6 | path="res://.godot/imported/screenshots.png-9f87be5e87bf3b9d4018aa31f97ec762.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://screenshots.png"
14 | dest_files=["res://.godot/imported/screenshots.png-9f87be5e87bf3b9d4018aa31f97ec762.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/icon.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dl6xfpj4d6kt5"
6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://icon.svg"
14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/dock_slot_expander/icon.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://du1ew3aoa1oxo"
6 | path="res://.godot/imported/icon.svg-9f3571057a8c41e302e2b2f2002a9e08.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/dock_slot_expander/icon.svg"
14 | dest_files=["res://.godot/imported/icon.svg-9f3571057a8c41e302e2b2f2002a9e08.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 bypell
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/addons/dock_slot_expander/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 bypell
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/addons/dock_slot_expander/icon.svg:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/addons/dock_slot_expander/dock_slot_expander.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorPlugin
3 |
4 | var use_ctrl_space := true
5 | var use_middle_mouse_click := true
6 |
7 | var _dock_slot_wrappers : Array[DockSlotWrapper]
8 | var _columns : Array[Node]
9 | var _expanded_dock_slot : TabContainer
10 |
11 |
12 | func _enter_tree() -> void:
13 | _fetch_dock_slots()
14 |
15 |
16 | func _exit_tree() -> void:
17 | _reset_dock_slots()
18 |
19 |
20 | func _input(event: InputEvent) -> void:
21 | if use_ctrl_space and event is InputEventKey:
22 | if event.keycode == KEY_SPACE and event.pressed and event.ctrl_pressed:
23 | var dock_slot := _get_dock_slot_at_position(get_viewport().get_mouse_position())
24 | _toggle_dock_slot(dock_slot)
25 | return
26 |
27 | if use_middle_mouse_click and event is InputEventMouseButton:
28 | if event.button_index == MOUSE_BUTTON_MIDDLE and event.pressed and not event.ctrl_pressed:
29 | var dock_slot := _get_dock_slot_at_position(get_viewport().get_mouse_position())
30 | _toggle_dock_slot(dock_slot)
31 | return
32 |
33 |
34 | func _toggle_dock_slot(dock_slot : TabContainer) -> void:
35 | # if no dock to toggle
36 | if not dock_slot:
37 | return
38 |
39 | # if no dock selected before this, expanding toggled dock
40 | if not _expanded_dock_slot:
41 | _expand_dock_slot(dock_slot)
42 | return
43 |
44 | # if dock toggled is same as currently expanded one, reset
45 | if _expanded_dock_slot and dock_slot == _expanded_dock_slot:
46 | _reset_dock_slots()
47 | return
48 |
49 | # if different dock toggled than one currently expanded, reset then expand new one
50 | if _expanded_dock_slot and not dock_slot == _expanded_dock_slot:
51 | _reset_dock_slots()
52 | _expand_dock_slot(dock_slot)
53 | return
54 |
55 |
56 | # hides vertical neighbor and opposing column of dock slot
57 | func _expand_dock_slot(dock_slot : TabContainer) -> void:
58 | _expanded_dock_slot = dock_slot
59 |
60 | var dock_slot_wrapper := _find_wrapper_for_dock_slot(dock_slot)
61 | if dock_slot_wrapper.opposing_column:
62 | dock_slot_wrapper.opposing_column.hide()
63 | if dock_slot_wrapper.vertical_neighbor:
64 | dock_slot_wrapper.vertical_neighbor.hide()
65 |
66 |
67 | # resets visibility of slots/columns to what it should be depending on content
68 | func _reset_dock_slots() -> void:
69 | _expanded_dock_slot = null
70 |
71 | for col in _columns:
72 | var at_least_one_dock_found := false
73 | for dock_slot in col.get_children():
74 | if dock_slot is TabContainer:
75 | if dock_slot.get_child_count() > 0:
76 | at_least_one_dock_found = true
77 | dock_slot.show()
78 | else:
79 | dock_slot.hide()
80 | col.visible = at_least_one_dock_found
81 |
82 |
83 | # call expand or reset depending on state of _expanded_dock_slot
84 | # this plugin is literally fighting with the editor for dock control
85 | func _refresh() -> void:
86 | if _expanded_dock_slot:
87 | _expand_dock_slot(_expanded_dock_slot)
88 | else:
89 | _reset_dock_slots()
90 |
91 |
92 | func _fetch_dock_slots() -> void:
93 | # find dock slots
94 | for dock_slot_index in DOCK_SLOT_MAX:
95 | var dummy_control := Control.new()
96 | add_control_to_dock(dock_slot_index, dummy_control)
97 |
98 | var dock_slot := dummy_control.get_parent()
99 | if dock_slot is TabContainer:
100 |
101 | # create wrapper for it with reference to parent column (vsplit)
102 | var wrapper : DockSlotWrapper = DockSlotWrapper.new(dock_slot)
103 | wrapper.column = dock_slot.get_parent()
104 | _dock_slot_wrappers.append(wrapper)
105 |
106 | # add column to array
107 | if not _columns.has(wrapper.column):
108 | _columns.append(wrapper.column)
109 | wrapper.column.child_order_changed.connect(_refresh)
110 |
111 | # signals that trigger a refresh
112 | dock_slot.active_tab_rearranged.connect(_refresh.unbind(1))
113 | dock_slot.tab_changed.connect(_refresh.unbind(1))
114 |
115 | remove_control_from_docks(dummy_control)
116 | dummy_control.free()
117 |
118 | # assign opposing column (the vsplit to hide when that slot is expanded)
119 | _dock_slot_wrappers[DOCK_SLOT_LEFT_UL].opposing_column = _dock_slot_wrappers[DOCK_SLOT_LEFT_UR].column
120 | _dock_slot_wrappers[DOCK_SLOT_LEFT_BL].opposing_column = _dock_slot_wrappers[DOCK_SLOT_LEFT_BR].column
121 | _dock_slot_wrappers[DOCK_SLOT_LEFT_UR].opposing_column = _dock_slot_wrappers[DOCK_SLOT_LEFT_UL].column
122 | _dock_slot_wrappers[DOCK_SLOT_LEFT_BR].opposing_column = _dock_slot_wrappers[DOCK_SLOT_LEFT_BL].column
123 | _dock_slot_wrappers[DOCK_SLOT_RIGHT_UL].opposing_column = _dock_slot_wrappers[DOCK_SLOT_RIGHT_UR].column
124 | _dock_slot_wrappers[DOCK_SLOT_RIGHT_BL].opposing_column = _dock_slot_wrappers[DOCK_SLOT_RIGHT_BR].column
125 | _dock_slot_wrappers[DOCK_SLOT_RIGHT_UR].opposing_column = _dock_slot_wrappers[DOCK_SLOT_RIGHT_UL].column
126 | _dock_slot_wrappers[DOCK_SLOT_RIGHT_BR].opposing_column = _dock_slot_wrappers[DOCK_SLOT_RIGHT_BL].column
127 |
128 | # assign neighbor
129 | for d in _dock_slot_wrappers:
130 | var neighbors := d.dock_slot.get_parent().get_children()
131 | neighbors.erase(d.dock_slot)
132 | var neighbor : TabContainer
133 | for n in neighbors:
134 | if n is TabContainer:
135 | neighbor = n
136 | if neighbor:
137 | d.vertical_neighbor = neighbor
138 |
139 |
140 | func _find_wrapper_for_dock_slot(dock_slot : TabContainer) -> DockSlotWrapper:
141 | if not dock_slot:
142 | return null
143 |
144 | for dock_slot_wrapper in _dock_slot_wrappers:
145 | if dock_slot_wrapper.dock_slot == dock_slot:
146 | return dock_slot_wrapper
147 | return null
148 |
149 |
150 | func _get_dock_slot_at_position(pos : Vector2) -> TabContainer:
151 | var dock_slot : TabContainer
152 |
153 | for d in _dock_slot_wrappers:
154 | if d.dock_slot.get_global_rect().has_point(pos) and d.dock_slot.is_visible_in_tree():
155 | return d.dock_slot
156 | return null
157 |
158 |
159 | class DockSlotWrapper:
160 | var dock_slot : TabContainer
161 | var vertical_neighbor : TabContainer
162 | var column : Node
163 | var opposing_column : Node
164 |
165 | func _init(p_node_reference : TabContainer = null) -> void:
166 | dock_slot = p_node_reference
167 |
--------------------------------------------------------------------------------