├── .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 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 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 | --------------------------------------------------------------------------------