└── addons └── script-ide ├── LICENSE ├── README.md ├── icon ├── class.svg ├── class.svg.import ├── constant.svg ├── constant.svg.import ├── engine_func.svg ├── engine_func.svg.import ├── export.svg ├── export.svg.import ├── func.svg ├── func.svg.import ├── func_get.svg ├── func_get.svg.import ├── func_set.svg ├── func_set.svg.import ├── property.svg ├── property.svg.import ├── signal.svg └── signal.svg.import ├── override ├── override_panel.gd └── override_panel.tscn ├── plugin.cfg ├── plugin.gd └── quickopen ├── quick_open_panel.gd └── quick_open_panel.tscn /addons/script-ide/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Marius Hanl 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/script-ide/README.md: -------------------------------------------------------------------------------- 1 | # Script IDE 2 | 3 | Transforms the Script UI into an IDE like UI. 4 | Tabs are used for navigating between scripts. 5 | The default Outline got an overhaul and now shows all members of the script (not just methods) with unique icons for faster navigation. 6 | Enhanced keyboard navigation for Scripts and Outline. 7 | Fast quick search functionality. 8 | Quick function Override functionality. 9 | 10 | Features: 11 | - Scripts are now shown as Tabs inside a TabContainer 12 | - The Outline got an overhaul and shows more than just the methods of the script. It includes the following members with a unique icon: 13 | - Classes (Red Square) 14 | - Constants (Red Circle) 15 | - Signals (Yellow) 16 | - Export variables (Orange) 17 | - (Static) Variables (Red) 18 | - Engine callback functions (Blue) 19 | - (Static) Functions (Green) 20 | - Setter functions (Green circle, with an arrow inside it pointing to the right) 21 | - Getter functions (Green circle, with an arrow inside it pointing to the left) 22 | - All the different members of the script can be hidden or made visible again by the outline filter. This allows fine control what should be visible (e.g. only signals, (Godot) functions, ...) 23 | - A `Right Click` enables only the clicked filter, another `Right Click` will enable all filters again 24 | - The Outline can be opened in a Popup with a defined shortcut for quick navigation between methods 25 | - You can navigate through the Outline with the `Arrow` keys (or `Page up/Page down`) and scroll to the selected item by pressing `ENTER` 26 | - Scripts can be opened in a Popup with a defined shortcut or when clicking the three dots on the top right of the TabContainer for quick navigation between scripts 27 | - The currently edited script is automatically selected in the Filesystem Dock 28 | - Files can be quickly searched by the Quick Search Popup with `Shift`+`Shift` 29 | - You can find and quickly override any method from your super classes with `Alt`+`Ins` 30 | - The plugin is written with performance in mind, everything is very fast and works without any lags or stuttering 31 | 32 | Customization: 33 | - The Outline is on the right side (can be changed to be on the left side again) 34 | - The Outline can be toggled via `File -> Toggle Scripts Panel`. This will hide or show it 35 | - The order in the Outline can be changed 36 | - There is also the possibility to hide private members, this is all members starting with a `_` 37 | - The Script ItemList is not visible by default, but can be made visible again 38 | 39 | All settings can be changed in the `Editor Settings` under `Plugin` -> `Script Ide`: 40 | - `Open Outline Popup` = Shortcut to control how the Outline Popup should be triggered (default=CTRL+O or META+O) 41 | - `Outline Position Right` = Flag to control whether the outline should be on the right or on the left side of the script editor (default=true) 42 | - `Outline Order` = List which specifies the order of all different types in the Outline 43 | - `Hide Private Members` = Flag to control whether private members (methods/variables/constants starting with '_') should be hidden in the Outline or not (default=false) 44 | - `Open Script Popup` = Shortcut to control how the Script Popup should be triggered (default=CTRL+U or META+U) 45 | - `Script List Visible` = Flag to control whether the script list should still be visible or not (above the outline) (default=false) 46 | - `Script Tabs Visible` = Flag to control whether the script tabs should be visible or not (default=true) 47 | - `Script Tabs Position Top` = Flag to control whether the script tabs should be on the top or on the bottom (default=true) 48 | - `Auto Navigate in FileSystem Dock` = Flag to control whether the script that is currently edited should be automatically selected in the Filesystem Dock (default=true) 49 | - `Open Quick Search Popup` = Shortcut to control how the Quick Search Popup should be triggered (default=Shift+Shift, double press behavior is hardcoded for now) 50 | - `Open Override Popup` = Shortcut to control how the Override Popup should be triggered (default=Alt+Ins) 51 | - `Cycle Tab forward` = Shortcut to cycle the script tabs in the forward direction (only works in the 'Script' Editor Tab) (default=CTRL+TAB) 52 | - `Cycle Tab backward` = Shortcut to cycle the script tabs in the backward direction (only works in the 'Script' Editor Tab) (default=CTRL+SHIFT+TAB) 53 | - All outline visibility settings 54 | 55 | ![Example of the Outline](https://github.com/user-attachments/assets/1729cb2b-01ae-4365-b77a-45edcb94b978) 56 | 57 | ![Example of the Outline Popup](https://github.com/user-attachments/assets/995c721f-9708-40d9-a4e8-57b1a99e9c29) 58 | 59 | ![Example of the TabContainer Script ItemList Popup](https://github.com/user-attachments/assets/484d498c-bd1c-4c77-a693-ac31a8500fbe) 60 | 61 | ![Example of the Script ItemList Popup](https://github.com/user-attachments/assets/bb976604-6049-4ce1-a28e-377fc62899f6) 62 | 63 | ![Example of the Quick Search Popup](https://github.com/user-attachments/assets/01141f05-e07c-4059-8d6f-e4c7490cbd40) 64 | 65 | ![Example of the Plugin Editor Settings](https://github.com/user-attachments/assets/0450e423-bc49-4076-862b-c95a62190df1) 66 | -------------------------------------------------------------------------------- /addons/script-ide/icon/class.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/class.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://csik7oxvt7tq3" 6 | path="res://.godot/imported/class.svg-e6f2816a1f06041fb421c2af52817a4a.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/class.svg" 15 | dest_files=["res://.godot/imported/class.svg-e6f2816a1f06041fb421c2af52817a4a.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/constant.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/constant.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cawc456ja8vf5" 6 | path="res://.godot/imported/constant.svg-f6e857276565573c7540f3c32801842a.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/constant.svg" 15 | dest_files=["res://.godot/imported/constant.svg-f6e857276565573c7540f3c32801842a.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/engine_func.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/engine_func.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cupb0polhqrwj" 6 | path="res://.godot/imported/engine_func.svg-91320e42f9cc7bdd7576002e82fa6ab8.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/engine_func.svg" 15 | dest_files=["res://.godot/imported/engine_func.svg-91320e42f9cc7bdd7576002e82fa6ab8.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/export.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/export.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bvu2gnj8fv2kw" 6 | path="res://.godot/imported/export.svg-d2d18132258a7a219ec1af1f0316c91c.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/export.svg" 15 | dest_files=["res://.godot/imported/export.svg-d2d18132258a7a219ec1af1f0316c91c.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/func.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/func.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://rni04cl446ov" 6 | path="res://.godot/imported/func.svg-139842caa5b4b7e4839711b6c756d0f7.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/func.svg" 15 | dest_files=["res://.godot/imported/func.svg-139842caa5b4b7e4839711b6c756d0f7.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/func_get.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/func_get.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c2a3aowyhxj5x" 6 | path="res://.godot/imported/func_get.svg-093f0ce02889d1f102ff9cc3e7f72654.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/func_get.svg" 15 | dest_files=["res://.godot/imported/func_get.svg-093f0ce02889d1f102ff9cc3e7f72654.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/func_set.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/func_set.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bvjkrti6kj6o2" 6 | path="res://.godot/imported/func_set.svg-c31168d90866ff1707ad9834754bd2c9.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/func_set.svg" 15 | dest_files=["res://.godot/imported/func_set.svg-c31168d90866ff1707ad9834754bd2c9.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/property.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/property.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dbwlgnwv5e8kl" 6 | path="res://.godot/imported/property.svg-9e228499f30651faad74aa99e4499d7e.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/property.svg" 15 | dest_files=["res://.godot/imported/property.svg-9e228499f30651faad74aa99e4499d7e.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/icon/signal.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/script-ide/icon/signal.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bnccvnaloqnte" 6 | path="res://.godot/imported/signal.svg-97182e1498b520a1ff5b8b9017c3b480.ctex" 7 | metadata={ 8 | "has_editor_variant": true, 9 | "vram_texture": false 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://addons/script-ide/icon/signal.svg" 15 | dest_files=["res://.godot/imported/signal.svg-97182e1498b520a1ff5b8b9017c3b480.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=0 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=false 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=1 36 | svg/scale=1.0 37 | editor/scale_with_editor_scale=true 38 | editor/convert_colors_with_editor_theme=false 39 | -------------------------------------------------------------------------------- /addons/script-ide/override/override_panel.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends PopupPanel 3 | 4 | const FUNC_META: StringName = &"func" 5 | 6 | @onready var filter_txt: LineEdit = %FilterTxt 7 | @onready var class_func_tree: Tree = %ClassFuncTree 8 | @onready var ok_btn: Button = %OkBtn 9 | @onready var cancel_btn: Button = %CancelBtn 10 | 11 | var plugin: EditorPlugin 12 | 13 | var selections: int = 0 14 | var class_to_functions: Dictionary[StringName, PackedStringArray] 15 | 16 | func _ready() -> void: 17 | filter_txt.text_changed.connect(update_tree_filter.unbind(1)) 18 | 19 | class_func_tree.multi_selected.connect(func(item: TreeItem, col: int, selected: bool): count_selection(selected)) 20 | class_func_tree.item_activated.connect(generate_functions) 21 | 22 | cancel_btn.pressed.connect(hide) 23 | ok_btn.pressed.connect(generate_functions) 24 | 25 | about_to_popup.connect(on_show) 26 | 27 | func update_tree_filter(): 28 | update_tree() 29 | 30 | func count_selection(selected: bool): 31 | if (selected): 32 | selections += 1 33 | else: 34 | selections -= 1 35 | 36 | ok_btn.disabled = selections == 0 37 | 38 | func on_show(): 39 | class_func_tree.clear() 40 | selections = 0 41 | ok_btn.disabled = true 42 | filter_txt.text = &"" 43 | 44 | var script: Script = EditorInterface.get_script_editor().get_current_script() 45 | class_to_functions = collect_all_class_functions(script) # [StringName, PackedStringArray] 46 | if (class_to_functions.is_empty()): 47 | return 48 | 49 | update_tree() 50 | filter_txt.grab_focus() 51 | 52 | func update_tree(): 53 | class_func_tree.clear() 54 | 55 | var text: String = filter_txt.text 56 | 57 | var root: TreeItem = class_func_tree.create_item() 58 | for class_name_str: StringName in class_to_functions.keys(): 59 | var class_item: TreeItem = root.create_child(0) 60 | class_item.set_selectable(0, false) 61 | class_item.set_text(0, class_name_str) 62 | 63 | for function: String in class_to_functions.get(class_name_str): 64 | if (text.is_empty() || text.is_subsequence_ofn(function)): 65 | var func_item: TreeItem = class_item.create_child() 66 | func_item.set_text(0, function) 67 | if (plugin.keywords.has(function.get_slice("(", 0))): 68 | func_item.set_icon(0, plugin.engine_func_icon) 69 | else: 70 | func_item.set_icon(0, plugin.func_icon) 71 | func_item.set_meta(FUNC_META, function) 72 | 73 | func collect_all_class_functions(script: Script) -> Dictionary[StringName, PackedStringArray]: 74 | var existing_funcs: Dictionary[String, int] = {} # Used as Set. 75 | for func_str: String in plugin.outline_cache.engine_funcs: 76 | existing_funcs[func_str] = 0 77 | for func_str: String in plugin.outline_cache.funcs: 78 | existing_funcs[func_str] = 0 79 | 80 | var class_to_functions: Dictionary[StringName, PackedStringArray] = collect_super_class_functions(script.get_base_script(), existing_funcs) 81 | var native_class_to_functions: Dictionary[StringName, PackedStringArray] = collect_native_class_functions(script.get_instance_base_type(), existing_funcs) 82 | 83 | return native_class_to_functions.merged(class_to_functions) 84 | 85 | func collect_super_class_functions(base_script: Script, existing_funcs: Dictionary[String, int]) -> Dictionary[StringName, PackedStringArray]: 86 | var super_classes: Array[Script] = [] 87 | while (base_script != null): 88 | super_classes.insert(0, base_script) 89 | 90 | base_script = base_script.get_base_script() 91 | 92 | var class_to_functions: Dictionary[StringName, PackedStringArray] = {} 93 | for super_class: Script in super_classes: 94 | var functions: PackedStringArray = collect_script_functions(super_class, existing_funcs) 95 | if (functions.is_empty()): 96 | continue 97 | 98 | class_to_functions[super_class.get_global_name()] = functions 99 | 100 | return class_to_functions 101 | 102 | func collect_native_class_functions(native_class: StringName, existing_funcs: Dictionary[String, int]) -> Dictionary[StringName, PackedStringArray]: 103 | var super_native_classes: Array[StringName] = [] 104 | while (native_class != &""): 105 | super_native_classes.insert(0, native_class) 106 | 107 | native_class = ClassDB.get_parent_class(native_class) 108 | 109 | var class_to_functions: Dictionary[StringName, PackedStringArray] = {} 110 | for super_native_class: StringName in super_native_classes: 111 | var functions: PackedStringArray = collect_class_functions(super_native_class, existing_funcs) 112 | if (functions.is_empty()): 113 | continue 114 | 115 | class_to_functions[super_native_class] = functions 116 | 117 | return class_to_functions 118 | 119 | func collect_class_functions(native_class: StringName, existing_funcs: Dictionary[String, int]): 120 | var functions: PackedStringArray = [] 121 | 122 | for method: Dictionary in ClassDB.class_get_method_list(native_class, true): 123 | if (method[&"flags"] & METHOD_FLAG_VIRTUAL <= 0): 124 | continue 125 | 126 | var func_name: String = method[&"name"] 127 | if (existing_funcs.has(func_name)): 128 | continue 129 | 130 | func_name = create_function_signature(method) 131 | functions.append(func_name) 132 | 133 | return functions 134 | 135 | func collect_script_functions(super_class: Script, existing_funcs: Dictionary[String, int]) -> PackedStringArray: 136 | var functions: PackedStringArray = [] 137 | 138 | for method: Dictionary in super_class.get_script_method_list(): 139 | var func_name: String = method[&"name"] 140 | if (existing_funcs.has(func_name)): 141 | continue 142 | 143 | existing_funcs[func_name] = 0 144 | 145 | func_name = create_function_signature(method) 146 | functions.append(func_name) 147 | 148 | return functions 149 | 150 | func create_function_signature(method: Dictionary) -> String: 151 | var func_name: String = method[&"name"] 152 | func_name += "(" 153 | 154 | var arg_str: String = "" 155 | for arg: Dictionary in method[&"args"]: 156 | if (arg_str != ""): 157 | arg_str += ", " 158 | 159 | arg_str += arg[&"name"] 160 | var type: String = get_type(arg) 161 | if (type != ""): 162 | arg_str += ": " + type 163 | 164 | func_name += arg_str + ")" 165 | 166 | var return_str: String = get_type(method[&"return"]) 167 | if (return_str != ""): 168 | func_name += " -> " + return_str 169 | 170 | return func_name 171 | 172 | func generate_functions(): 173 | var selected_item: TreeItem = class_func_tree.get_next_selected(null) 174 | if (selected_item == null): 175 | return 176 | 177 | var selected_functions: PackedStringArray = [] 178 | 179 | while (selected_item != null): 180 | var function: String = selected_item.get_meta(FUNC_META) 181 | selected_functions.append(function) 182 | 183 | selected_item = class_func_tree.get_next_selected(selected_item) 184 | 185 | var generated_text: String = "" 186 | for function: String in selected_functions: 187 | generated_text += "\nfunc " + function + ":\n\tpass\n" 188 | 189 | var editor: CodeEdit = EditorInterface.get_script_editor().get_current_editor().get_base_editor() 190 | editor.text += generated_text 191 | 192 | plugin.goto_line(editor.get_line_count() - 1) 193 | 194 | hide() 195 | 196 | func get_type(dict: Dictionary) -> String: 197 | var type: String = dict[&"class_name"] 198 | if (type != &""): 199 | return type 200 | 201 | var type_hint: int = dict[&"type"] 202 | if (type_hint == 0): 203 | return &"" 204 | 205 | type = type_string(type_hint) 206 | # Dictionary 207 | if (type_hint == 27): 208 | var generic: String = dict[&"hint_string"] 209 | if (generic != &""): 210 | var generic_parts: PackedStringArray = generic.split(";") 211 | if (generic_parts.size() == 2): 212 | return type + "[" + generic_parts[0] + ", " + generic_parts[1] + "]" 213 | 214 | # Array 215 | if (type_hint == 28): 216 | var generic: String = dict[&"hint_string"] 217 | if (generic != &""): 218 | return type + "[" + generic + "]" 219 | 220 | return type 221 | -------------------------------------------------------------------------------- /addons/script-ide/override/override_panel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bb1n82qxlqanh"] 2 | 3 | [ext_resource type="Script" uid="uid://jwla7ovpvhdw" path="res://addons/script-ide/override/override_panel.gd" id="1_c3eqr"] 4 | 5 | [node name="OverridePanel" type="PopupPanel"] 6 | size = Vector2i(551, 194) 7 | visible = true 8 | script = ExtResource("1_c3eqr") 9 | 10 | [node name="PanelContainer" type="PanelContainer" parent="."] 11 | anchors_preset = 15 12 | anchor_right = 1.0 13 | anchor_bottom = 1.0 14 | offset_left = 9.0 15 | offset_top = 9.0 16 | offset_right = -9.0 17 | offset_bottom = -9.0 18 | grow_horizontal = 2 19 | grow_vertical = 2 20 | 21 | [node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] 22 | layout_mode = 2 23 | 24 | [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] 25 | layout_mode = 2 26 | theme_override_constants/separation = 4 27 | 28 | [node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] 29 | layout_mode = 2 30 | text = "Select Functions to Override/Implement" 31 | 32 | [node name="FilterTxt" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer"] 33 | unique_name_in_owner = true 34 | layout_mode = 2 35 | placeholder_text = "Filter Methods" 36 | 37 | [node name="ClassFuncTree" type="Tree" parent="PanelContainer/MarginContainer/VBoxContainer"] 38 | unique_name_in_owner = true 39 | layout_mode = 2 40 | size_flags_vertical = 3 41 | hide_root = true 42 | select_mode = 2 43 | 44 | [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] 45 | layout_mode = 2 46 | theme_override_constants/separation = 4 47 | alignment = 2 48 | 49 | [node name="OkBtn" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 50 | unique_name_in_owner = true 51 | custom_minimum_size = Vector2(64, 0) 52 | layout_mode = 2 53 | text = "Ok" 54 | 55 | [node name="CancelBtn" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 56 | unique_name_in_owner = true 57 | custom_minimum_size = Vector2(64, 0) 58 | layout_mode = 2 59 | text = "Cancel" 60 | -------------------------------------------------------------------------------- /addons/script-ide/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Script-IDE" 4 | description="Transforms the Script UI into an IDE like UI. Tabs are used for navigating between scripts. The default Outline got an overhaul and now shows all members of the script (not just methods) with unique icons for faster navigation. Enhanced keyboard navigation for Scripts and Outline. Fast quick search functionality." 5 | author="Marius Hanl" 6 | version="1.9.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/script-ide/plugin.gd: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2023-present Marius Hanl under the MIT License. 2 | ## The editor plugin entrypoint for Script-IDE. 3 | ## 4 | ## The Script Tabs and Outline modifies the code that is inside 'script_editor_plugin.cpp'. 5 | ## That is, the structure is changed a little bit. 6 | ## The internals of then native C++ code are therefore important in order to make this plugin work 7 | ## without interfering with the Engine. 8 | ## All the other functionality does not modify anything Engine related. 9 | ## 10 | ## Script-IDE does not use global class_name's in order to not clutter projects using it. 11 | ## Especially since this is an editor only plugin, we do not want this plugin in the final game. 12 | ## Therefore, code that references other code inside this plugin is untyped. 13 | @tool 14 | extends EditorPlugin 15 | 16 | const GETTER: StringName = &"get" 17 | const SETTER: StringName = &"set" 18 | const UNDERSCORE: StringName = &"_" 19 | const INLINE: StringName = &"@" 20 | 21 | const BUILT_IN_SCRIPT: StringName = &"::GDScript" 22 | 23 | #region Settings and Shortcuts 24 | ## Editor setting path 25 | const SCRIPT_IDE: StringName = &"plugin/script_ide/" 26 | ## Editor setting for the outline position 27 | const OUTLINE_POSITION_RIGHT: StringName = SCRIPT_IDE + &"outline_position_right" 28 | ## Editor setting to control the order of the outline 29 | const OUTLINE_ORDER: StringName = SCRIPT_IDE + &"outline_order" 30 | ## Editor setting to control whether private members (annotated with '_' should be hidden or not) 31 | const HIDE_PRIVATE_MEMBERS: StringName = SCRIPT_IDE + &"hide_private_members" 32 | ## Editor setting to control whether we want to auto navigate to the script 33 | ## in the filesystem (dock) when selected 34 | const AUTO_NAVIGATE_IN_FS: StringName = SCRIPT_IDE + &"auto_navigate_in_filesystem_dock" 35 | ## Editor setting to control whether the script list should be visible or not 36 | const SCRIPT_LIST_VISIBLE: StringName = SCRIPT_IDE + &"script_list_visible" 37 | ## Editor setting to control whether the script tabs should be visible or not. 38 | const SCRIPT_TABS_VISIBLE: StringName = SCRIPT_IDE + &"script_tabs_visible" 39 | ## Editor setting to control where the script tabs should be. 40 | const SCRIPT_TAB_POSITION_TOP: StringName = SCRIPT_IDE + &"script_tab_position_top" 41 | 42 | ## Editor setting for the 'Open Outline Popup' shortcut 43 | const OPEN_OUTLINE_POPUP: StringName = SCRIPT_IDE + &"open_outline_popup" 44 | ## Editor setting for the 'Open Scripts Popup' shortcut 45 | const OPEN_SCRIPTS_POPUP: StringName = SCRIPT_IDE + &"open_scripts_popup" 46 | ## Editor setting for the 'Open Scripts Popup' shortcut 47 | const OPEN_QUICK_SEARCH_POPUP: StringName = SCRIPT_IDE + &"open_quick_search_popup" 48 | ## Editor setting for the 'Open Override Popup' shortcut 49 | const OPEN_OVERRIDE_POPUP: StringName = SCRIPT_IDE + &"open_override_popup" 50 | ## Editor setting for the 'Tab cycle forward' shortcut 51 | const TAB_CYCLE_FORWARD: StringName = SCRIPT_IDE + &"tab_cycle_forward" 52 | ## Editor setting for the 'Tab cycle backward' shortcut 53 | const TAB_CYCLE_BACKWARD: StringName = SCRIPT_IDE + &"tab_cycle_backward" 54 | #endregion 55 | 56 | #region Outline type name and icon 57 | const ENGINE_FUNCS: StringName = &"Engine Callbacks" 58 | const FUNCS: StringName = &"Functions" 59 | const SIGNALS: StringName = &"Signals" 60 | const EXPORTED: StringName = &"Exported Properties" 61 | const PROPERTIES: StringName = &"Properties" 62 | const CLASSES: StringName = &"Classes" 63 | const CONSTANTS: StringName = &"Constants" 64 | 65 | var engine_func_icon: Texture2D 66 | var func_icon: Texture2D 67 | var func_get_icon: Texture2D 68 | var func_set_icon: Texture2D 69 | var property_icon: Texture2D 70 | var export_icon: Texture2D 71 | var signal_icon: Texture2D 72 | var constant_icon: Texture2D 73 | var class_icon: Texture2D 74 | #endregion 75 | 76 | #region Editor settings 77 | var is_outline_right: bool = true 78 | var is_script_list_visible: bool = false 79 | var hide_private_members: bool = false 80 | var is_auto_navigate_in_fs: bool = true 81 | var is_script_tabs_visible: bool = true 82 | var is_script_tabs_top: bool = true 83 | var outline_order: PackedStringArray 84 | 85 | var open_outline_popup_shc: Shortcut 86 | var open_scripts_popup_shc: Shortcut 87 | var open_quick_search_popup_shc: Shortcut 88 | var open_override_popup_shc: Shortcut 89 | var tab_cycle_forward_shc: Shortcut 90 | var tab_cycle_backward_shc: Shortcut 91 | #endregion 92 | 93 | #region Existing controls we modify 94 | var outline_container: Control 95 | var outline_parent: Control 96 | var scripts_tab_container: TabContainer 97 | var scripts_tab_bar: TabBar 98 | var script_filter_txt: LineEdit 99 | var scripts_item_list: ItemList 100 | var panel_container: VSplitContainer 101 | 102 | var split_container: HSplitContainer 103 | var old_outline: ItemList 104 | var outline_filter_txt: LineEdit 105 | var sort_btn: Button 106 | #endregion 107 | 108 | #region Own controls we add 109 | var outline: ItemList 110 | var outline_popup: PopupPanel 111 | var filter_box: HBoxContainer 112 | 113 | var scripts_popup: PopupPanel 114 | var quick_open_popup: PopupPanel 115 | var override_popup: PopupPanel 116 | 117 | var class_btn: Button 118 | var constant_btn: Button 119 | var signal_btn: Button 120 | var property_btn: Button 121 | var export_btn: Button 122 | var func_btn: Button 123 | var engine_func_btn: Button 124 | #endregion 125 | 126 | #region Plugin variables 127 | var keywords: Dictionary[String, int] = {} # Used as Set. 128 | var outline_type_order: Array[OutlineType] = [] 129 | var outline_cache: OutlineCache 130 | var tab_state: TabStateCache 131 | 132 | var old_script_editor_base: ScriptEditorBase 133 | var old_script_type: StringName 134 | 135 | var selected_tab: int = -1 136 | var last_tab_hovered: int = -1 137 | var sync_script_list: bool = false 138 | var file_to_navigate: String = &"" 139 | var suppress_settings_sync: bool = false 140 | 141 | const QUICK_OPEN_INTERVAL: int = 400 142 | var quick_open_tween: Tween 143 | #endregion 144 | 145 | #region Plugin Enter / Exit setup 146 | ## Change the Godot script UI and transform into an IDE like UI 147 | func _enter_tree() -> void: 148 | init_icons() 149 | init_settings() 150 | init_shortcuts() 151 | 152 | # Update on filesystem changed (e.g. save operation). 153 | var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem() 154 | file_system.filesystem_changed.connect(schedule_update) 155 | 156 | # Sync settings changes for this plugin. 157 | get_editor_settings().settings_changed.connect(sync_settings) 158 | 159 | var script_editor: ScriptEditor = EditorInterface.get_script_editor() 160 | 161 | # Change script item list visibility (based on settings). 162 | scripts_item_list = find_or_null(script_editor.find_children("*", "ItemList", true, false)) 163 | scripts_item_list.allow_reselect = true 164 | scripts_item_list.item_selected.connect(hide_scripts_popup.unbind(1)) 165 | update_script_list_visibility() 166 | 167 | # Add script filter navigation. 168 | script_filter_txt = find_or_null(scripts_item_list.get_parent().find_children("*", "LineEdit", true, false)) 169 | script_filter_txt.gui_input.connect(navigate_on_list.bind(scripts_item_list, select_script)) 170 | 171 | # Make tab container visible. 172 | scripts_tab_container = find_or_null(script_editor.find_children("*", "TabContainer", true, false)) 173 | scripts_tab_bar = scripts_tab_container.get_tab_bar() 174 | 175 | # Save old tab state to restore later. 176 | tab_state = TabStateCache.new() 177 | tab_state.save(scripts_tab_container, scripts_tab_bar) 178 | 179 | # Create and set script popup. 180 | create_set_scripts_popup() 181 | 182 | # Configure tab container and bar. 183 | scripts_tab_container.tabs_visible = is_script_tabs_visible 184 | scripts_tab_container.drag_to_rearrange_enabled = true 185 | scripts_tab_container.auto_translate_mode = Node.AUTO_TRANSLATE_MODE_DISABLED 186 | update_tabs_position() 187 | 188 | scripts_tab_bar.tab_close_display_policy = TabBar.CLOSE_BUTTON_SHOW_ACTIVE_ONLY 189 | scripts_tab_bar.drag_to_rearrange_enabled = true 190 | scripts_tab_bar.select_with_rmb = true 191 | scripts_tab_bar.tab_close_pressed.connect(on_tab_close) 192 | scripts_tab_bar.tab_rmb_clicked.connect(on_tab_rmb) 193 | scripts_tab_bar.tab_hovered.connect(on_tab_hovered) 194 | scripts_tab_bar.mouse_exited.connect(on_tab_bar_mouse_exited) 195 | scripts_tab_bar.active_tab_rearranged.connect(on_active_tab_rearranged) 196 | scripts_tab_bar.gui_input.connect(on_tab_bar_gui_input) 197 | 198 | scripts_tab_bar.tab_changed.connect(on_tab_changed) 199 | 200 | # Remove existing outline and add own outline. 201 | split_container = find_or_null(script_editor.find_children("*", "HSplitContainer", true, false)) 202 | outline_container = split_container.get_child(0) 203 | 204 | if (is_outline_right): 205 | update_outline_position() 206 | 207 | old_outline = find_or_null(outline_container.find_children("*", "ItemList", true, false), 1) 208 | outline_parent = old_outline.get_parent() 209 | outline_parent.remove_child(old_outline) 210 | 211 | outline = ItemList.new() 212 | outline.allow_reselect = true 213 | outline.size_flags_vertical = Control.SIZE_EXPAND_FILL 214 | outline_parent.add_child(outline) 215 | 216 | outline.item_selected.connect(scroll_outline) 217 | 218 | # Add a filter box for all kind of members 219 | filter_box = HBoxContainer.new() 220 | 221 | engine_func_btn = create_filter_btn(engine_func_icon, ENGINE_FUNCS) 222 | func_btn = create_filter_btn(func_icon, FUNCS) 223 | signal_btn = create_filter_btn(signal_icon, SIGNALS) 224 | export_btn = create_filter_btn(export_icon, EXPORTED) 225 | property_btn = create_filter_btn(property_icon, PROPERTIES) 226 | class_btn = create_filter_btn(class_icon, CLASSES) 227 | constant_btn = create_filter_btn(constant_icon, CONSTANTS) 228 | update_outline_button_order() 229 | 230 | outline.get_parent().add_child(filter_box) 231 | outline.get_parent().move_child(filter_box, outline.get_index()) 232 | 233 | # Add navigation to the filter and text filtering. 234 | outline_filter_txt = find_or_null(outline_container.find_children("*", "LineEdit", true, false), 1) 235 | outline_filter_txt.gui_input.connect(navigate_on_list.bind(outline, scroll_outline)) 236 | outline_filter_txt.text_changed.connect(update_outline.unbind(1)) 237 | 238 | # Add callback when the sorting changed. 239 | sort_btn = find_or_null(outline_container.find_children("*", "Button", true, false)) 240 | sort_btn.pressed.connect(update_outline) 241 | 242 | on_tab_changed(scripts_tab_bar.current_tab) 243 | 244 | ## Restore the old Godot script UI and free everything we created 245 | func _exit_tree() -> void: 246 | var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem() 247 | file_system.filesystem_changed.disconnect(schedule_update) 248 | 249 | if (old_script_editor_base != null): 250 | old_script_editor_base.edited_script_changed.disconnect(update_selected_tab) 251 | 252 | if (split_container != null): 253 | if (split_container != outline_container.get_parent()): 254 | split_container.add_child(outline_container) 255 | 256 | # Try to restore the previous split offset. 257 | if (is_outline_right): 258 | var split_offset: float = split_container.get_child(1).size.x 259 | split_container.split_offset = split_offset 260 | 261 | split_container.move_child(outline_container, 0) 262 | 263 | outline_filter_txt.gui_input.disconnect(navigate_on_list) 264 | outline_filter_txt.text_changed.disconnect(update_outline) 265 | sort_btn.pressed.disconnect(update_outline) 266 | 267 | outline.item_selected.disconnect(scroll_outline) 268 | 269 | outline_parent.remove_child(filter_box) 270 | outline_parent.remove_child(outline) 271 | outline_parent.add_child(old_outline) 272 | outline_parent.move_child(old_outline, 2) 273 | 274 | filter_box.free() 275 | outline.free() 276 | 277 | if (scripts_tab_container != null): 278 | tab_state.restore(scripts_tab_container, scripts_tab_bar) 279 | 280 | scripts_tab_container.pre_popup_pressed.disconnect(prepare_scripts_popup) 281 | scripts_tab_container.set_popup(null) 282 | scripts_popup.free() 283 | 284 | if (scripts_tab_bar != null): 285 | scripts_tab_bar.mouse_exited.disconnect(on_tab_bar_mouse_exited) 286 | scripts_tab_bar.gui_input.disconnect(on_tab_bar_gui_input) 287 | scripts_tab_bar.tab_close_pressed.disconnect(on_tab_close) 288 | scripts_tab_bar.tab_rmb_clicked.disconnect(on_tab_rmb) 289 | scripts_tab_bar.tab_hovered.disconnect(on_tab_hovered) 290 | scripts_tab_bar.active_tab_rearranged.disconnect(on_active_tab_rearranged) 291 | 292 | scripts_tab_bar.tab_changed.disconnect(on_tab_changed) 293 | 294 | if (scripts_item_list != null): 295 | scripts_item_list.allow_reselect = false 296 | scripts_item_list.item_selected.disconnect(hide_scripts_popup) 297 | scripts_item_list.get_parent().visible = true 298 | 299 | if (script_filter_txt != null): 300 | script_filter_txt.gui_input.disconnect(navigate_on_list) 301 | 302 | if (outline_popup != null): 303 | outline_popup.free() 304 | if (quick_open_popup != null): 305 | quick_open_popup.free() 306 | if (override_popup != null): 307 | override_popup.free() 308 | 309 | get_editor_settings().settings_changed.disconnect(sync_settings) 310 | #endregion 311 | 312 | #region Plugin and Shortcut processing 313 | ## Lazy pattern to update the editor only once per frame 314 | func _process(delta: float) -> void: 315 | update_editor() 316 | set_process(false) 317 | 318 | ## Process the user defined shortcuts 319 | func _shortcut_input(event: InputEvent) -> void: 320 | if (!event.is_pressed() || event.is_echo()): 321 | return 322 | 323 | if (open_outline_popup_shc.matches_event(event)): 324 | get_viewport().set_input_as_handled() 325 | open_outline_popup() 326 | elif (open_scripts_popup_shc.matches_event(event)): 327 | get_viewport().set_input_as_handled() 328 | open_scripts_popup() 329 | elif (open_quick_search_popup_shc.matches_event(event)): 330 | if (quick_open_tween != null && quick_open_tween.is_running()): 331 | get_viewport().set_input_as_handled() 332 | if (quick_open_tween != null): 333 | quick_open_tween.kill() 334 | 335 | quick_open_tween = create_tween() 336 | quick_open_tween.tween_interval(0.1) 337 | quick_open_tween.tween_callback(open_quick_search_popup) 338 | quick_open_tween.tween_callback(func(): quick_open_tween = null) 339 | else: 340 | quick_open_tween = create_tween() 341 | quick_open_tween.tween_interval(QUICK_OPEN_INTERVAL / 1000.0) 342 | quick_open_tween.tween_callback(func(): quick_open_tween = null) 343 | elif (open_override_popup_shc.matches_event(event)): 344 | get_viewport().set_input_as_handled() 345 | open_override_popup() 346 | elif (EditorInterface.get_script_editor().is_visible_in_tree()): 347 | if (tab_cycle_forward_shc.matches_event(event)): 348 | get_viewport().set_input_as_handled() 349 | 350 | var new_tab: int = scripts_tab_container.current_tab + 1 351 | if (new_tab == scripts_tab_container.get_tab_count()): 352 | new_tab = 0 353 | scripts_tab_container.current_tab = new_tab 354 | elif (tab_cycle_backward_shc.matches_event(event)): 355 | get_viewport().set_input_as_handled() 356 | 357 | var new_tab: int = scripts_tab_container.current_tab - 1 358 | if (new_tab == -1): 359 | new_tab = scripts_tab_container.get_tab_count() - 1 360 | scripts_tab_container.current_tab = new_tab 361 | 362 | ## May cancels the quick search shortcut timer. 363 | func _input(event: InputEvent) -> void: 364 | if (event is InputEventKey): 365 | if (!open_quick_search_popup_shc.matches_event(event)): 366 | if (quick_open_tween != null): 367 | quick_open_tween.kill() 368 | quick_open_tween = null 369 | #endregion 370 | 371 | #region Icon, Settings, Shortcut initialization 372 | ## Initializes all plugin icons, while respecting the editor settings. 373 | func init_icons(): 374 | engine_func_icon = create_editor_texture(load_rel("icon/engine_func.svg")) 375 | func_icon = create_editor_texture(load_rel("icon/func.svg")) 376 | func_get_icon = create_editor_texture(load_rel("icon/func_get.svg")) 377 | func_set_icon = create_editor_texture(load_rel("icon/func_set.svg")) 378 | property_icon = create_editor_texture(load_rel("icon/property.svg")) 379 | export_icon = create_editor_texture(load_rel("icon/export.svg")) 380 | signal_icon = create_editor_texture(load_rel("icon/signal.svg")) 381 | constant_icon = create_editor_texture(load_rel("icon/constant.svg")) 382 | class_icon = create_editor_texture(load_rel("icon/class.svg")) 383 | 384 | ## Initializes all settings. 385 | ## Every setting can be changed while this plugin is active, which will override them. 386 | func init_settings(): 387 | is_outline_right = get_setting(OUTLINE_POSITION_RIGHT, is_outline_right) 388 | hide_private_members = get_setting(HIDE_PRIVATE_MEMBERS, hide_private_members) 389 | is_script_list_visible = get_setting(SCRIPT_LIST_VISIBLE, is_script_list_visible) 390 | is_auto_navigate_in_fs = get_setting(AUTO_NAVIGATE_IN_FS, is_auto_navigate_in_fs) 391 | is_script_tabs_visible = get_setting(SCRIPT_TABS_VISIBLE, is_script_tabs_visible) 392 | is_script_tabs_top = get_setting(SCRIPT_TAB_POSITION_TOP, is_script_tabs_top) 393 | 394 | init_outline_order() 395 | 396 | ## Initializes the outline type structure and sorts it based off the outline order. 397 | func init_outline_order(): 398 | var outline_type: OutlineType = OutlineType.new() 399 | outline_type.type_name = ENGINE_FUNCS 400 | outline_type.add_to_outline = func(): add_to_outline_if_selected(engine_func_btn, 401 | func(): add_to_outline(outline_cache.engine_funcs, engine_func_icon, &"func")) 402 | outline_type_order.append(outline_type) 403 | 404 | outline_type = OutlineType.new() 405 | outline_type.type_name = FUNCS 406 | outline_type.add_to_outline = func(): add_to_outline_if_selected(func_btn, 407 | func(): add_to_outline_ext(outline_cache.funcs, get_func_icon, &"func", &"static")) 408 | outline_type_order.append(outline_type) 409 | 410 | outline_type = OutlineType.new() 411 | outline_type.type_name = SIGNALS 412 | outline_type.add_to_outline = func(): add_to_outline_if_selected(signal_btn, 413 | func(): add_to_outline(outline_cache.signals, signal_icon, &"signal")) 414 | outline_type_order.append(outline_type) 415 | 416 | outline_type = OutlineType.new() 417 | outline_type.type_name = EXPORTED 418 | outline_type.add_to_outline = func(): add_to_outline_if_selected(export_btn, 419 | func(): add_to_outline(outline_cache.exports, export_icon, &"var", &"@export")) 420 | outline_type_order.append(outline_type) 421 | 422 | outline_type = OutlineType.new() 423 | outline_type.type_name = PROPERTIES 424 | outline_type.add_to_outline = func(): add_to_outline_if_selected(property_btn, 425 | func(): add_to_outline(outline_cache.properties, property_icon, &"var")) 426 | outline_type_order.append(outline_type) 427 | 428 | outline_type = OutlineType.new() 429 | outline_type.type_name = CLASSES 430 | outline_type.add_to_outline = func(): add_to_outline_if_selected(class_btn, 431 | func(): add_to_outline(outline_cache.classes, class_icon, &"class")) 432 | outline_type_order.append(outline_type) 433 | 434 | outline_type = OutlineType.new() 435 | outline_type.type_name = CONSTANTS 436 | outline_type.add_to_outline = func(): add_to_outline_if_selected(constant_btn, 437 | func(): add_to_outline(outline_cache.constants, constant_icon, &"const", &"enum")) 438 | outline_type_order.append(outline_type) 439 | 440 | update_outline_order() 441 | 442 | func update_outline_button_order(): 443 | var all_buttons: Array[Button] = [engine_func_btn, func_btn, signal_btn, export_btn, property_btn, class_btn, constant_btn] 444 | all_buttons.sort_custom(sort_buttons_by_outline_order) 445 | 446 | for btn: Button in all_buttons: 447 | if (btn.get_parent() != null): 448 | filter_box.remove_child(btn) 449 | 450 | for btn: Button in all_buttons: 451 | filter_box.add_child(btn) 452 | 453 | func update_outline_order(): 454 | var editor_settings: EditorSettings = get_editor_settings() 455 | if (editor_settings.has_setting(OUTLINE_ORDER)): 456 | outline_order = editor_settings.get_setting(OUTLINE_ORDER) 457 | else: 458 | outline_order = [ENGINE_FUNCS, FUNCS, SIGNALS, EXPORTED, PROPERTIES, CONSTANTS, CLASSES] 459 | editor_settings.set_setting(OUTLINE_ORDER, outline_order) 460 | 461 | outline_type_order.sort_custom(sort_types_by_outline_order) 462 | 463 | func sort_buttons_by_outline_order(btn1: Button, btn2: Button) -> bool: 464 | return sort_by_outline_order(btn1.tooltip_text, btn2.tooltip_text) 465 | 466 | func sort_types_by_outline_order(type1: OutlineType, type2: OutlineType) -> bool: 467 | return sort_by_outline_order(type1.type_name, type2.type_name) 468 | 469 | func sort_by_outline_order(outline_type1: StringName, outline_type2: StringName) -> bool: 470 | return outline_order.find(outline_type1) < outline_order.find(outline_type2) 471 | 472 | ## Initializes all shortcuts. 473 | ## Every shortcut can be changed while this plugin is active, which will override them. 474 | func init_shortcuts(): 475 | var editor_settings: EditorSettings = get_editor_settings() 476 | if (!editor_settings.has_setting(OPEN_OUTLINE_POPUP)): 477 | var shortcut: Shortcut = Shortcut.new() 478 | var event: InputEventKey = InputEventKey.new() 479 | event.device = -1 480 | event.command_or_control_autoremap = true 481 | event.keycode = KEY_O 482 | 483 | shortcut.events = [ event ] 484 | editor_settings.set_setting(OPEN_OUTLINE_POPUP, shortcut) 485 | 486 | if (!editor_settings.has_setting(OPEN_SCRIPTS_POPUP)): 487 | var shortcut: Shortcut = Shortcut.new() 488 | var event: InputEventKey = InputEventKey.new() 489 | event.device = -1 490 | event.command_or_control_autoremap = true 491 | event.keycode = KEY_U 492 | 493 | shortcut.events = [ event ] 494 | editor_settings.set_setting(OPEN_SCRIPTS_POPUP, shortcut) 495 | 496 | if (!editor_settings.has_setting(OPEN_QUICK_SEARCH_POPUP)): 497 | var shortcut: Shortcut = Shortcut.new() 498 | var event: InputEventKey = InputEventKey.new() 499 | event.device = -1 500 | event.keycode = KEY_SHIFT 501 | 502 | shortcut.events = [ event ] 503 | editor_settings.set_setting(OPEN_QUICK_SEARCH_POPUP, shortcut) 504 | 505 | if (!editor_settings.has_setting(OPEN_OVERRIDE_POPUP)): 506 | var shortcut: Shortcut = Shortcut.new() 507 | var event: InputEventKey = InputEventKey.new() 508 | event.device = -1 509 | event.keycode = KEY_INSERT 510 | event.alt_pressed = true 511 | 512 | shortcut.events = [ event ] 513 | editor_settings.set_setting(OPEN_OVERRIDE_POPUP, shortcut) 514 | 515 | if (!editor_settings.has_setting(TAB_CYCLE_FORWARD)): 516 | var shortcut: Shortcut = Shortcut.new() 517 | var event: InputEventKey = InputEventKey.new() 518 | event.device = -1 519 | event.keycode = KEY_TAB 520 | event.ctrl_pressed = true 521 | 522 | shortcut.events = [ event ] 523 | editor_settings.set_setting(TAB_CYCLE_FORWARD, shortcut) 524 | 525 | if (!editor_settings.has_setting(TAB_CYCLE_BACKWARD)): 526 | var shortcut: Shortcut = Shortcut.new() 527 | var event: InputEventKey = InputEventKey.new() 528 | event.device = -1 529 | event.keycode = KEY_TAB 530 | event.shift_pressed = true 531 | event.ctrl_pressed = true 532 | 533 | shortcut.events = [ event ] 534 | editor_settings.set_setting(TAB_CYCLE_BACKWARD, shortcut) 535 | 536 | open_outline_popup_shc = editor_settings.get_setting(OPEN_OUTLINE_POPUP) 537 | open_scripts_popup_shc = editor_settings.get_setting(OPEN_SCRIPTS_POPUP) 538 | open_quick_search_popup_shc = editor_settings.get_setting(OPEN_QUICK_SEARCH_POPUP) 539 | open_override_popup_shc = editor_settings.get_setting(OPEN_OVERRIDE_POPUP) 540 | tab_cycle_forward_shc = editor_settings.get_setting(TAB_CYCLE_FORWARD) 541 | tab_cycle_backward_shc = editor_settings.get_setting(TAB_CYCLE_BACKWARD) 542 | #endregion 543 | 544 | ## Schedules an update on the next frame 545 | func schedule_update(): 546 | set_process(true) 547 | 548 | ## Updates all parts of the editor that are needed to be synchronized with the file system change. 549 | func update_editor(): 550 | update_script_text_filter() 551 | 552 | if (sync_script_list): 553 | if (file_to_navigate != &""): 554 | EditorInterface.get_file_system_dock().navigate_to_path(file_to_navigate) 555 | EditorInterface.get_script_editor().get_current_editor().get_base_editor().grab_focus() 556 | file_to_navigate = &"" 557 | 558 | sync_tab_with_script_list() 559 | sync_script_list = false 560 | 561 | update_tabs() 562 | update_outline_cache() 563 | update_outline() 564 | 565 | func add_to_outline_if_selected(btn: Button, action: Callable): 566 | if (btn.button_pressed): 567 | action.call() 568 | 569 | func open_quick_search_popup(): 570 | if (quick_open_popup == null): 571 | quick_open_popup = load_rel("quickopen/quick_open_panel.tscn").instantiate() 572 | quick_open_popup.plugin = self 573 | 574 | if (quick_open_popup.get_parent() != null): 575 | quick_open_popup.get_parent().remove_child(quick_open_popup) 576 | quick_open_popup.popup_exclusive_on_parent(EditorInterface.get_script_editor(), get_center_editor_rect()) 577 | 578 | func open_override_popup(): 579 | var script: Script = get_current_script() 580 | if (!script): 581 | return 582 | 583 | if (override_popup == null): 584 | override_popup = load_rel("override/override_panel.tscn").instantiate() 585 | override_popup.plugin = self 586 | 587 | if (override_popup.get_parent() != null): 588 | override_popup.get_parent().remove_child(override_popup) 589 | override_popup.popup_exclusive_on_parent(EditorInterface.get_script_editor(), get_center_editor_rect()) 590 | 591 | func hide_scripts_popup(): 592 | if (scripts_popup != null && scripts_popup.visible): 593 | scripts_popup.hide.call_deferred() 594 | 595 | func create_set_scripts_popup(): 596 | panel_container = scripts_item_list.get_parent().get_parent() 597 | 598 | scripts_popup = PopupPanel.new() 599 | scripts_popup.popup_hide.connect(restore_scripts_list) 600 | 601 | # Need to be inside the tree, so it can be shown as popup for the tab container. 602 | var script_editor: ScriptEditor = EditorInterface.get_script_editor() 603 | script_editor.add_child(scripts_popup) 604 | 605 | scripts_tab_container.pre_popup_pressed.connect(prepare_scripts_popup) 606 | scripts_tab_container.set_popup(scripts_popup) 607 | 608 | func prepare_scripts_popup(): 609 | scripts_popup.size.x = outline.size.x 610 | scripts_popup.size.y = panel_container.size.y - scripts_tab_bar.size.y 611 | 612 | scripts_item_list.get_parent().reparent(scripts_popup) 613 | scripts_item_list.get_parent().visible = true 614 | 615 | script_filter_txt.grab_focus() 616 | 617 | func restore_scripts_list(): 618 | script_filter_txt.text = &"" 619 | 620 | update_script_list_visibility() 621 | 622 | scripts_item_list.get_parent().reparent(panel_container) 623 | panel_container.move_child(scripts_item_list.get_parent(), 0) 624 | 625 | func navigate_on_list(event: InputEvent, list: ItemList, submit: Callable): 626 | if (event.is_action_pressed(&"ui_text_submit")): 627 | var index: int = get_list_index(list) 628 | if (index == -1): 629 | return 630 | 631 | submit.call(index) 632 | elif (event.is_action_pressed(&"ui_down", true)): 633 | var index: int = get_list_index(list) 634 | if (index == list.item_count - 1): 635 | return 636 | 637 | navigate_list(list, index, 1) 638 | elif (event.is_action_pressed(&"ui_up", true)): 639 | var index: int = get_list_index(list) 640 | if (index <= 0): 641 | return 642 | 643 | navigate_list(list, index, -1) 644 | elif (event.is_action_pressed(&"ui_page_down", true)): 645 | var index: int = get_list_index(list) 646 | if (index == list.item_count - 1): 647 | return 648 | 649 | navigate_list(list, index, 5) 650 | elif (event.is_action_pressed(&"ui_page_up", true)): 651 | var index: int = get_list_index(list) 652 | if (index <= 0): 653 | return 654 | 655 | navigate_list(list, index, -5) 656 | elif (event is InputEventKey && list.item_count > 0 && !list.is_anything_selected()): 657 | list.select(0) 658 | 659 | func get_list_index(list: ItemList) -> int: 660 | var items: PackedInt32Array = list.get_selected_items() 661 | 662 | if (items.is_empty()): 663 | return -1 664 | 665 | return items[0] 666 | 667 | func navigate_list(list: ItemList, index: int, amount: int): 668 | index = clamp(index + amount, 0, list.item_count - 1) 669 | 670 | list.select(index) 671 | list.ensure_current_is_visible() 672 | list.accept_event() 673 | 674 | func get_center_editor_rect() -> Rect2i: 675 | var script_editor: ScriptEditor = EditorInterface.get_script_editor() 676 | 677 | var size: Vector2i = Vector2i(400, 500) 678 | var x: int 679 | var y: int 680 | 681 | if (script_editor.get_parent().get_parent() is Window): 682 | # Floating editor. 683 | var window: Window = script_editor.get_parent().get_parent() 684 | var window_rect: Rect2 = window.get_visible_rect() 685 | 686 | x = window_rect.size.x / 2 - size.x / 2 687 | y = window_rect.size.y / 2 - size.y / 2 688 | else: 689 | x = script_editor.global_position.x + script_editor.size.x / 2 - size.x / 2 690 | y = script_editor.global_position.y + script_editor.size.y / 2 - size.y / 2 691 | 692 | return Rect2i(Vector2i(x, y), size) 693 | 694 | func open_outline_popup(): 695 | var button_flags: Array[bool] = [] 696 | for child: Node in filter_box.get_children(): 697 | var btn: Button = child 698 | button_flags.append(btn.button_pressed) 699 | 700 | btn.set_pressed_no_signal(true) 701 | 702 | var old_text: String = outline_filter_txt.text 703 | outline_filter_txt.text = &"" 704 | 705 | if (outline_popup == null): 706 | outline_popup = PopupPanel.new() 707 | 708 | var outline_initially_closed: bool = !outline_container.visible 709 | if (outline_initially_closed): 710 | outline_container.visible = true 711 | 712 | outline_container.reparent(outline_popup) 713 | 714 | outline_popup.popup_hide.connect(on_outline_popup_hidden.bind(outline_initially_closed, old_text, button_flags)) 715 | 716 | if (outline_popup.get_parent() != null): 717 | outline_popup.get_parent().remove_child(outline_popup) 718 | outline_popup.popup_exclusive_on_parent(EditorInterface.get_script_editor(), get_center_editor_rect()) 719 | 720 | update_outline() 721 | outline_filter_txt.grab_focus() 722 | 723 | func on_outline_popup_hidden(outline_initially_closed: bool, old_text: String, button_flags: Array[bool]): 724 | outline_popup.popup_hide.disconnect(on_outline_popup_hidden) 725 | 726 | if outline_initially_closed: 727 | outline_container.visible = false 728 | 729 | outline_container.reparent(split_container) 730 | if (!is_outline_right): 731 | split_container.move_child(outline_container, 0) 732 | 733 | outline_filter_txt.text = old_text 734 | 735 | var index: int = 0 736 | for flag: bool in button_flags: 737 | var btn: Button = filter_box.get_child(index) 738 | btn.set_pressed_no_signal(flag) 739 | index += 1 740 | 741 | update_outline() 742 | 743 | func open_scripts_popup(): 744 | scripts_item_list.get_parent().reparent(scripts_popup) 745 | scripts_item_list.get_parent().visible = true 746 | 747 | if (scripts_popup.get_parent() != null): 748 | scripts_popup.get_parent().remove_child(scripts_popup) 749 | scripts_popup.popup_exclusive_on_parent(EditorInterface.get_script_editor(), get_center_editor_rect()) 750 | 751 | script_filter_txt.grab_focus() 752 | 753 | ## Removes the script filter text and emits the signal so that the tabs stay 754 | ## and we do not break anything there. 755 | func update_script_text_filter(): 756 | if (script_filter_txt.text != &""): 757 | script_filter_txt.text = &"" 758 | script_filter_txt.text_changed.emit(&"") 759 | 760 | func get_current_script() -> Script: 761 | var script_editor: ScriptEditor = EditorInterface.get_script_editor() 762 | return script_editor.get_current_script() 763 | 764 | func select_script(selected_idx: int): 765 | hide_scripts_popup() 766 | 767 | scripts_item_list.item_selected.emit(selected_idx) 768 | 769 | func scroll_outline(selected_idx: int): 770 | if (outline_popup != null && outline_popup.visible): 771 | outline_popup.hide.call_deferred() 772 | 773 | var script: Script = get_current_script() 774 | if (!script): 775 | return 776 | 777 | var text: String = outline.get_item_text(selected_idx) 778 | var metadata: Dictionary[StringName, StringName] = outline.get_item_metadata(selected_idx) 779 | var modifier: StringName = metadata[&"modifier"] 780 | var type: StringName = metadata[&"type"] 781 | 782 | var type_with_text: String = type + " " + text 783 | if (type == &"func"): 784 | type_with_text = type_with_text + "(" 785 | 786 | var source_code: String = script.get_source_code() 787 | var lines: PackedStringArray = source_code.split("\n") 788 | 789 | var index: int = 0 790 | for line: String in lines: 791 | # Easy case, like 'var abc' 792 | if (line.begins_with(type_with_text)): 793 | goto_line(index) 794 | return 795 | 796 | # We have an modifier, e.g. 'static' 797 | if (modifier != &"" && line.begins_with(modifier)): 798 | if (line.begins_with(modifier + " " + type_with_text)): 799 | goto_line(index) 800 | return 801 | # Special case: An 'enum' is treated different. 802 | elif (modifier == &"enum" && line.contains("enum " + text)): 803 | goto_line(index) 804 | return 805 | 806 | # Hard case, probably something like '@onready var abc' 807 | if (type == &"var" && line.contains(type_with_text)): 808 | goto_line(index) 809 | return 810 | 811 | index += 1 812 | 813 | push_error(type_with_text + " or " + modifier + " not found in source code") 814 | 815 | func goto_line(index: int): 816 | var script_editor: ScriptEditor = EditorInterface.get_script_editor() 817 | script_editor.goto_line(index) 818 | 819 | var code_edit: CodeEdit = script_editor.get_current_editor().get_base_editor() 820 | code_edit.set_caret_line(index) 821 | code_edit.set_v_scroll(index) 822 | code_edit.set_caret_column(code_edit.get_line(index).length()) 823 | code_edit.set_h_scroll(0) 824 | 825 | code_edit.grab_focus() 826 | 827 | func create_filter_btn(icon: Texture2D, title: StringName) -> Button: 828 | var btn: Button = Button.new() 829 | btn.toggle_mode = true 830 | btn.icon = icon 831 | btn.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER 832 | btn.tooltip_text = title 833 | 834 | var property: StringName = SCRIPT_IDE + title.to_lower().replace(" ", "_") 835 | btn.set_meta(&"property", property) 836 | btn.button_pressed = get_setting(property, true) 837 | 838 | btn.toggled.connect(on_filter_button_pressed.bind(btn)) 839 | btn.gui_input.connect(on_right_click.bind(btn)) 840 | 841 | btn.add_theme_color_override(&"icon_pressed_color", Color.WHITE) 842 | btn.add_theme_color_override(&"icon_hover_color", Color.WHITE) 843 | btn.add_theme_color_override(&"icon_hover_pressed_color", Color.WHITE) 844 | btn.add_theme_color_override(&"icon_focus_color", Color.WHITE) 845 | 846 | var style_box_empty: StyleBoxEmpty = StyleBoxEmpty.new() 847 | btn.add_theme_stylebox_override(&"normal", style_box_empty) 848 | 849 | var style_box: StyleBoxFlat = StyleBoxFlat.new() 850 | style_box.draw_center = false 851 | style_box.border_color = get_editor_accent_color() 852 | style_box.set_border_width_all(1 * get_editor_scale()) 853 | style_box.set_corner_radius_all(get_editor_corner_radius() * get_editor_scale()) 854 | btn.add_theme_stylebox_override(&"focus", style_box) 855 | 856 | return btn 857 | 858 | func on_right_click(event: InputEvent, btn: Button): 859 | if !(event is InputEventMouseButton): 860 | return 861 | 862 | var mouse_event: InputEventMouseButton = event 863 | 864 | if (!mouse_event.is_pressed() || mouse_event.button_index != MOUSE_BUTTON_RIGHT): 865 | return 866 | 867 | btn.button_pressed = true 868 | 869 | var pressed_state: bool = false 870 | for child: Node in filter_box.get_children(): 871 | var other_btn: Button = child 872 | 873 | if (btn != other_btn): 874 | pressed_state = pressed_state || other_btn.button_pressed 875 | 876 | for child: Node in filter_box.get_children(): 877 | var other_btn: Button = child 878 | 879 | if (btn != other_btn): 880 | other_btn.button_pressed = !pressed_state 881 | 882 | outline_filter_txt.grab_focus() 883 | 884 | func on_filter_button_pressed(pressed: bool, btn: Button): 885 | set_setting(btn.get_meta(&"property"), pressed) 886 | 887 | update_outline() 888 | outline_filter_txt.grab_focus() 889 | 890 | func update_outline_position(): 891 | if (is_outline_right): 892 | # Try to restore the previous split offset. 893 | var split_offset: float = split_container.get_child(1).size.x 894 | split_container.split_offset = split_offset 895 | split_container.move_child(outline_container, 1) 896 | else: 897 | split_container.move_child(outline_container, 0) 898 | 899 | func update_script_list_visibility(): 900 | scripts_item_list.get_parent().visible = is_script_list_visible 901 | 902 | func create_editor_texture(texture: Texture2D) -> Texture2D: 903 | var image: Image = texture.get_image().duplicate() 904 | image.adjust_bcs(1.0, 1.0, get_editor_icon_saturation()) 905 | 906 | return ImageTexture.create_from_image(image) 907 | 908 | func sync_settings(): 909 | if (suppress_settings_sync): 910 | return 911 | 912 | var changed_settings: PackedStringArray = get_editor_settings().get_changed_settings() 913 | for setting: String in changed_settings: 914 | if (setting == "interface/theme/icon_saturation"): 915 | init_icons() 916 | engine_func_btn.icon = engine_func_icon 917 | func_btn.icon = func_icon 918 | signal_btn.icon = signal_icon 919 | export_btn.icon = export_icon 920 | property_btn.icon = property_icon 921 | class_btn.icon = class_icon 922 | constant_btn.icon = constant_icon 923 | update_outline() 924 | continue 925 | 926 | if (!setting.begins_with(SCRIPT_IDE)): 927 | continue 928 | 929 | if (setting == OUTLINE_POSITION_RIGHT): 930 | var new_outline_right: bool = get_setting(OUTLINE_POSITION_RIGHT, is_outline_right) 931 | if (new_outline_right != is_outline_right): 932 | is_outline_right = new_outline_right 933 | 934 | update_outline_position() 935 | elif (setting == OUTLINE_ORDER): 936 | update_outline_order() 937 | update_outline_button_order() 938 | update_outline() 939 | elif (setting == HIDE_PRIVATE_MEMBERS): 940 | var new_hide_private_members: bool = get_setting(HIDE_PRIVATE_MEMBERS, hide_private_members) 941 | if (new_hide_private_members != hide_private_members): 942 | hide_private_members = new_hide_private_members 943 | 944 | update_outline_cache() 945 | update_outline() 946 | elif (setting == SCRIPT_LIST_VISIBLE): 947 | var new_script_list_visible: bool = get_setting(SCRIPT_LIST_VISIBLE, is_script_list_visible) 948 | if (new_script_list_visible != is_script_list_visible): 949 | is_script_list_visible = new_script_list_visible 950 | 951 | update_script_list_visibility() 952 | elif (setting == SCRIPT_TABS_VISIBLE): 953 | var new_script_tabs_visible: bool = get_setting(SCRIPT_TABS_VISIBLE, is_script_tabs_visible) 954 | if (new_script_tabs_visible != is_script_tabs_visible): 955 | is_script_tabs_visible = new_script_tabs_visible 956 | 957 | scripts_tab_container.tabs_visible = is_script_tabs_visible 958 | elif (setting == SCRIPT_TAB_POSITION_TOP): 959 | var new_script_tabs_top: bool = get_setting(SCRIPT_TAB_POSITION_TOP, is_script_tabs_top) 960 | if (new_script_tabs_top != is_script_tabs_top): 961 | is_script_tabs_top = new_script_tabs_top 962 | 963 | update_tabs_position() 964 | elif (setting == AUTO_NAVIGATE_IN_FS): 965 | is_auto_navigate_in_fs = get_setting(AUTO_NAVIGATE_IN_FS, is_auto_navigate_in_fs) 966 | elif (setting == OPEN_OUTLINE_POPUP): 967 | open_outline_popup_shc = get_shortcut(OPEN_OUTLINE_POPUP) 968 | elif (setting == OPEN_SCRIPTS_POPUP): 969 | open_scripts_popup_shc = get_shortcut(OPEN_SCRIPTS_POPUP) 970 | elif (setting == OPEN_OVERRIDE_POPUP): 971 | open_override_popup_shc = get_shortcut(OPEN_OVERRIDE_POPUP) 972 | elif (setting == TAB_CYCLE_FORWARD): 973 | tab_cycle_forward_shc = get_shortcut(TAB_CYCLE_FORWARD) 974 | elif (setting == TAB_CYCLE_BACKWARD): 975 | tab_cycle_backward_shc = get_shortcut(TAB_CYCLE_BACKWARD) 976 | else: 977 | # Update filter buttons. 978 | for btn_node: Node in filter_box.get_children(): 979 | var btn: Button = btn_node 980 | var property: StringName = btn.get_meta(&"property") 981 | 982 | btn.button_pressed = get_setting(property, btn.button_pressed) 983 | 984 | func get_setting(property: StringName, alt: bool) -> bool: 985 | var editor_settings: EditorSettings = get_editor_settings() 986 | if (editor_settings.has_setting(property)): 987 | return editor_settings.get_setting(property) 988 | else: 989 | editor_settings.set_setting(property, alt) 990 | return alt 991 | 992 | func set_setting(property: StringName, value: bool): 993 | var editor_settings: EditorSettings = get_editor_settings() 994 | 995 | suppress_settings_sync = true 996 | editor_settings.set_setting(property, value) 997 | suppress_settings_sync = false 998 | 999 | func get_shortcut(property: StringName) -> Shortcut: 1000 | return get_editor_settings().get_setting(property) 1001 | 1002 | func on_tab_changed(index: int): 1003 | selected_tab = index; 1004 | 1005 | if (old_script_editor_base != null): 1006 | old_script_editor_base.edited_script_changed.disconnect(update_selected_tab) 1007 | old_script_editor_base = null 1008 | 1009 | var script_editor: ScriptEditor = EditorInterface.get_script_editor() 1010 | var script_editor_base: ScriptEditorBase = script_editor.get_current_editor() 1011 | 1012 | if (script_editor_base != null): 1013 | script_editor_base.edited_script_changed.connect(update_selected_tab) 1014 | 1015 | old_script_editor_base = script_editor_base 1016 | 1017 | sync_script_list = true 1018 | 1019 | if (is_auto_navigate_in_fs && script_editor.get_current_script() != null): 1020 | var file: String = script_editor.get_current_script().get_path() 1021 | 1022 | if (file.contains(BUILT_IN_SCRIPT)): 1023 | # We navigate to the scene in case of a built-in script. 1024 | file = file.get_slice(BUILT_IN_SCRIPT, 0) 1025 | 1026 | file_to_navigate = file 1027 | else: 1028 | file_to_navigate = &"" 1029 | 1030 | schedule_update() 1031 | 1032 | func update_selected_tab(): 1033 | if (selected_tab == -1): 1034 | return 1035 | 1036 | if (scripts_item_list.item_count == 0): 1037 | return 1038 | 1039 | update_tab(selected_tab) 1040 | 1041 | func update_tabs(): 1042 | for index: int in scripts_tab_container.get_tab_count(): 1043 | update_tab(index) 1044 | 1045 | func update_tab(index: int): 1046 | scripts_tab_container.set_tab_title(index, scripts_item_list.get_item_text(index)) 1047 | scripts_tab_container.set_tab_icon(index, scripts_item_list.get_item_icon(index)) 1048 | scripts_tab_container.set_tab_tooltip(index, scripts_item_list.get_item_tooltip(index)) 1049 | 1050 | func update_tabs_position(): 1051 | if (is_script_tabs_top): 1052 | scripts_tab_container.tabs_position = TabContainer.POSITION_TOP 1053 | else: 1054 | scripts_tab_container.tabs_position = TabContainer.POSITION_BOTTOM 1055 | 1056 | func update_keywords(script: Script): 1057 | if (script == null): 1058 | return 1059 | 1060 | var new_script_type: StringName = script.get_instance_base_type() 1061 | if (old_script_type != new_script_type): 1062 | old_script_type = new_script_type 1063 | 1064 | keywords.clear() 1065 | keywords["_static_init"] = 0 1066 | register_virtual_methods(new_script_type) 1067 | 1068 | func register_virtual_methods(clazz: String): 1069 | for method: Dictionary in ClassDB.class_get_method_list(clazz): 1070 | if (method[&"flags"] & METHOD_FLAG_VIRTUAL > 0): 1071 | keywords[method[&"name"]] = 0 1072 | 1073 | func update_outline_cache(): 1074 | outline_cache = null 1075 | 1076 | var script: Script = get_current_script() 1077 | if (!script): 1078 | return 1079 | 1080 | update_keywords(script) 1081 | 1082 | # Check if built-in script. In this case we need to duplicate it for whatever reason. 1083 | if (script.get_path().contains(BUILT_IN_SCRIPT)): 1084 | script = script.duplicate() 1085 | 1086 | outline_cache = OutlineCache.new() 1087 | 1088 | # Collect all script members. 1089 | for_each_script_member(script, func(array: Array[String], item: String): array.append(item)) 1090 | 1091 | # Remove script members that only exist in the base script (which includes the base of the base etc.). 1092 | # Note: The method that only collects script members without including the base script(s) 1093 | # is not exposed to GDScript. 1094 | var base_script: Script = script.get_base_script() 1095 | if (base_script != null): 1096 | for_each_script_member(base_script, func(array: Array[String], item: String): array.erase(item)) 1097 | 1098 | func for_each_script_member(script: Script, consumer: Callable): 1099 | # Functions / Methods 1100 | for dict: Dictionary in script.get_script_method_list(): 1101 | var func_name: String = dict[&"name"] 1102 | 1103 | if (keywords.has(func_name)): 1104 | consumer.call(outline_cache.engine_funcs, func_name) 1105 | else: 1106 | if hide_private_members && func_name.begins_with(UNDERSCORE): 1107 | continue 1108 | 1109 | # Inline getter/setter will normally be shown as '@...getter', '@...setter'. 1110 | # Since we already show the variable itself, we will skip those. 1111 | if (func_name.begins_with(INLINE)): 1112 | continue 1113 | 1114 | consumer.call(outline_cache.funcs, func_name) 1115 | 1116 | # Properties / Exported variables 1117 | for dict: Dictionary in script.get_script_property_list(): 1118 | var property: String = dict[&"name"] 1119 | if hide_private_members && property.begins_with(UNDERSCORE): 1120 | continue 1121 | 1122 | var usage: int = dict[&"usage"] 1123 | 1124 | if (usage & PROPERTY_USAGE_SCRIPT_VARIABLE): 1125 | if (usage & PROPERTY_USAGE_STORAGE && usage & PROPERTY_USAGE_EDITOR): 1126 | consumer.call(outline_cache.exports, property) 1127 | else: 1128 | consumer.call(outline_cache.properties, property) 1129 | 1130 | # Static variables (are separated for whatever reason) 1131 | for dict: Dictionary in script.get_property_list(): 1132 | var property: String = dict[&"name"] 1133 | if hide_private_members && property.begins_with(UNDERSCORE): 1134 | continue 1135 | 1136 | var usage: int = dict[&"usage"] 1137 | 1138 | if (usage & PROPERTY_USAGE_SCRIPT_VARIABLE): 1139 | consumer.call(outline_cache.properties, property) 1140 | 1141 | # Signals 1142 | for dict: Dictionary in script.get_script_signal_list(): 1143 | var signal_name: String = dict[&"name"] 1144 | 1145 | consumer.call(outline_cache.signals, signal_name) 1146 | 1147 | # Constants / Classes 1148 | for name_key: String in script.get_script_constant_map(): 1149 | if hide_private_members && name_key.begins_with(UNDERSCORE): 1150 | continue 1151 | 1152 | var object: Variant = script.get_script_constant_map().get(name_key) 1153 | # Inner classes have no source code, while a const of type GDScript has. 1154 | if (object is GDScript && !object.has_source_code()): 1155 | consumer.call(outline_cache.classes, name_key) 1156 | else: 1157 | consumer.call(outline_cache.constants, name_key) 1158 | 1159 | func update_outline(): 1160 | outline.clear() 1161 | 1162 | if (outline_cache == null): 1163 | return 1164 | 1165 | for outline_type: OutlineType in outline_type_order: 1166 | outline_type.add_to_outline.call() 1167 | 1168 | func add_to_outline(items: Array[String], icon: Texture2D, type: StringName, modifier: StringName = &""): 1169 | add_to_outline_ext(items, func(str: String): return icon, type, modifier) 1170 | 1171 | func add_to_outline_ext(items: Array[String], icon_callable: Callable, type: StringName, modifier: StringName = &""): 1172 | var text: String = outline_filter_txt.get_text() 1173 | 1174 | if (is_sorted()): 1175 | items = items.duplicate() 1176 | items.sort_custom(func(str1: String, str2: String): return str1.naturalnocasecmp_to(str2) < 0) 1177 | 1178 | for item: String in items: 1179 | if (text.is_empty() || text.is_subsequence_ofn(item)): 1180 | var icon: Texture2D = icon_callable.call(item) 1181 | outline.add_item(item, icon, true) 1182 | 1183 | var dict: Dictionary[StringName, StringName] = { 1184 | &"type": type, 1185 | &"modifier": modifier 1186 | } 1187 | outline.set_item_metadata(outline.item_count - 1, dict) 1188 | 1189 | func get_func_icon(func_name: String) -> Texture2D: 1190 | var icon: Texture2D = func_icon 1191 | if (func_name.begins_with(GETTER)): 1192 | icon = func_get_icon 1193 | elif (func_name.begins_with(SETTER)): 1194 | icon = func_set_icon 1195 | 1196 | return icon 1197 | 1198 | func sync_tab_with_script_list(): 1199 | # For some reason the selected tab is wrong. Looks like a Godot bug. 1200 | if (selected_tab >= scripts_item_list.item_count): 1201 | selected_tab = scripts_tab_bar.current_tab 1202 | 1203 | # Hide filter and outline for non .gd scripts. 1204 | var is_script: bool = get_current_script() != null 1205 | filter_box.visible = is_script 1206 | outline.visible = is_script 1207 | 1208 | # Sync with script item list. 1209 | if (selected_tab != -1 && scripts_item_list.item_count > 0 && !scripts_item_list.is_selected(selected_tab)): 1210 | scripts_item_list.select(selected_tab) 1211 | scripts_item_list.item_selected.emit(selected_tab) 1212 | 1213 | scripts_item_list.ensure_current_is_visible() 1214 | 1215 | func on_tab_bar_mouse_exited(): 1216 | last_tab_hovered = -1 1217 | 1218 | func on_tab_hovered(idx: int): 1219 | last_tab_hovered = idx 1220 | 1221 | func on_tab_bar_gui_input(event: InputEvent): 1222 | if (last_tab_hovered == -1): 1223 | return 1224 | 1225 | if (event is InputEventMouseButton): 1226 | if event.is_pressed() and event.button_index == MOUSE_BUTTON_MIDDLE: 1227 | update_script_text_filter() 1228 | simulate_item_clicked(last_tab_hovered, MOUSE_BUTTON_MIDDLE) 1229 | 1230 | if (last_tab_hovered >= scripts_tab_bar.tab_count - 1): 1231 | last_tab_hovered = -1 1232 | 1233 | func on_active_tab_rearranged(idx_to: int): 1234 | var control: Control = scripts_tab_container.get_tab_control(selected_tab) 1235 | if (!control): 1236 | return 1237 | 1238 | scripts_tab_container.move_child(control, idx_to) 1239 | scripts_tab_container.current_tab = scripts_tab_container.current_tab 1240 | selected_tab = scripts_tab_container.current_tab 1241 | 1242 | func get_res_path(idx: int) -> String: 1243 | var tab_control: Control = scripts_tab_container.get_tab_control(idx) 1244 | if (tab_control == null): 1245 | return '' 1246 | 1247 | var path_var: Variant = tab_control.get(&"metadata/_edit_res_path") 1248 | if (path_var == null): 1249 | return '' 1250 | 1251 | return path_var 1252 | 1253 | func on_tab_rmb(tab_idx: int): 1254 | update_script_text_filter() 1255 | simulate_item_clicked(tab_idx, MOUSE_BUTTON_RIGHT) 1256 | 1257 | func on_tab_close(tab_idx: int): 1258 | update_script_text_filter() 1259 | simulate_item_clicked(tab_idx, MOUSE_BUTTON_MIDDLE) 1260 | 1261 | func simulate_item_clicked(tab_idx: int, mouse_idx: int): 1262 | scripts_item_list.item_clicked.emit(tab_idx, scripts_item_list.get_local_mouse_position(), mouse_idx) 1263 | 1264 | func get_editor_scale() -> float: 1265 | return EditorInterface.get_editor_scale() 1266 | 1267 | func get_editor_corner_radius() -> int: 1268 | return EditorInterface.get_editor_settings().get_setting("interface/theme/corner_radius") 1269 | 1270 | func get_editor_accent_color() -> Color: 1271 | return EditorInterface.get_editor_settings().get_setting("interface/theme/accent_color") 1272 | 1273 | func get_editor_icon_saturation() -> float: 1274 | return EditorInterface.get_editor_settings().get_setting("interface/theme/icon_saturation") 1275 | 1276 | func is_sorted() -> bool: 1277 | return get_editor_settings().get_setting("text_editor/script_list/sort_members_outline_alphabetically") 1278 | 1279 | func get_editor_settings() -> EditorSettings: 1280 | return EditorInterface.get_editor_settings() 1281 | 1282 | func load_rel(path: String) -> Variant: 1283 | var script_path: String = get_script().get_path().get_base_dir() 1284 | return load(script_path.path_join(path)) 1285 | 1286 | static func find_or_null(arr: Array[Node], index: int = 0) -> Node: 1287 | if (arr.is_empty()): 1288 | push_error("""Node that is needed for Script-IDE not found. 1289 | Plugin will not work correctly. 1290 | This might be due to some other plugins or changes in the Engine. 1291 | Please report this to Script-IDE, so we can figure out a fix.""") 1292 | return null 1293 | return arr[index] 1294 | 1295 | ## Cache for everything inside we collected to show in the Outline. 1296 | class OutlineCache: 1297 | var classes: Array[String] = [] 1298 | var constants: Array[String] = [] 1299 | var signals: Array[String] = [] 1300 | var exports: Array[String] = [] 1301 | var properties: Array[String] = [] 1302 | var funcs: Array[String] = [] 1303 | var engine_funcs: Array[String] = [] 1304 | 1305 | ## Outline type for a concrete button with their items in the Outline. 1306 | class OutlineType: 1307 | var type_name: StringName 1308 | var add_to_outline: Callable 1309 | 1310 | ## Contains everything we modify on the Tab Control. Used to save and restore the behaviour 1311 | ## to keep the Engine in a clean state when the plugin is disabled. 1312 | class TabStateCache: 1313 | var tabs_visible: bool 1314 | var drag_to_rearrange_enabled: bool 1315 | var auto_translate_mode_state: Node.AutoTranslateMode 1316 | var tab_bar_drag_to_rearrange_enabled: bool 1317 | var tab_close_display_policy: TabBar.CloseButtonDisplayPolicy 1318 | var select_with_rmb: bool 1319 | 1320 | func save(tab_container: TabContainer, tab_bar: TabBar): 1321 | if (tab_container != null): 1322 | tabs_visible = tab_container.tabs_visible 1323 | drag_to_rearrange_enabled = tab_container.drag_to_rearrange_enabled 1324 | auto_translate_mode_state = tab_container.auto_translate_mode 1325 | if (tab_bar != null): 1326 | tab_bar_drag_to_rearrange_enabled = tab_bar.drag_to_rearrange_enabled 1327 | tab_close_display_policy = tab_bar.tab_close_display_policy 1328 | select_with_rmb = tab_bar.select_with_rmb 1329 | 1330 | func restore(tab_container: TabContainer, tab_bar: TabBar): 1331 | if (tab_container != null): 1332 | tab_container.tabs_visible = tabs_visible 1333 | tab_container.drag_to_rearrange_enabled = drag_to_rearrange_enabled 1334 | tab_container.auto_translate_mode = auto_translate_mode_state 1335 | if (tab_bar != null): 1336 | tab_bar.drag_to_rearrange_enabled = drag_to_rearrange_enabled 1337 | tab_bar.tab_close_display_policy = tab_close_display_policy 1338 | tab_bar.select_with_rmb = select_with_rmb 1339 | -------------------------------------------------------------------------------- /addons/script-ide/quickopen/quick_open_panel.gd: -------------------------------------------------------------------------------- 1 | ## Quick open panel to quickly access all resources that are in the project. 2 | ## Initially shows all resources, but can be changed to more specific resources 3 | ## or filtered down with text. 4 | @tool 5 | extends PopupPanel 6 | 7 | const ADDONS: StringName = &"res://addons" 8 | const SEPARATOR: StringName = &" - " 9 | const STRUCTURE_START: StringName = &"(" 10 | const STRUCTURE_END: StringName = &")" 11 | 12 | #region UI 13 | @onready var filter_bar: TabBar = %FilterBar 14 | @onready var search_option_btn: OptionButton = %SearchOptionBtn 15 | @onready var filter_txt: LineEdit = %FilterTxt 16 | @onready var files_list: ItemList = %FilesList 17 | #endregion 18 | 19 | var plugin: EditorPlugin 20 | 21 | var scenes: Array[FileData] 22 | var scripts: Array[FileData] 23 | var resources: Array[FileData] 24 | var others: Array[FileData] 25 | 26 | # For performance and memory considerations, we add all files into one reusable array. 27 | var all_files: Array[FileData] 28 | 29 | var is_rebuild_cache: bool = true 30 | 31 | #region Plugin and Shortcut processing 32 | func _ready() -> void: 33 | files_list.item_selected.connect(open_file) 34 | search_option_btn.item_selected.connect(rebuild_cache_and_ui.unbind(1)) 35 | filter_txt.text_changed.connect(fill_files_list.unbind(1)) 36 | 37 | filter_bar.tab_changed.connect(change_fill_files_list.unbind(1)) 38 | 39 | about_to_popup.connect(on_show) 40 | 41 | var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem() 42 | file_system.filesystem_changed.connect(schedule_rebuild) 43 | 44 | if (plugin != null): 45 | filter_txt.gui_input.connect(plugin.navigate_on_list.bind(files_list, open_file)) 46 | 47 | func _shortcut_input(event: InputEvent) -> void: 48 | if (!event.is_pressed() || event.is_echo()): 49 | return 50 | 51 | if (plugin.tab_cycle_forward_shc.matches_event(event)): 52 | get_viewport().set_input_as_handled() 53 | 54 | var new_tab: int = filter_bar.current_tab + 1 55 | if (new_tab == filter_bar.get_tab_count()): 56 | new_tab = 0 57 | filter_bar.current_tab = new_tab 58 | elif (plugin.tab_cycle_backward_shc.matches_event(event)): 59 | get_viewport().set_input_as_handled() 60 | 61 | var new_tab: int = filter_bar.current_tab - 1 62 | if (new_tab == -1): 63 | new_tab = filter_bar.get_tab_count() - 1 64 | filter_bar.current_tab = new_tab 65 | #endregion 66 | 67 | func open_file(index: int): 68 | hide() 69 | 70 | var file: String = files_list.get_item_metadata(index) 71 | 72 | if (ResourceLoader.exists(file)): 73 | var res: Resource = load(file) 74 | EditorInterface.edit_resource(res) 75 | 76 | if (res is PackedScene): 77 | EditorInterface.open_scene_from_path(file) 78 | 79 | func schedule_rebuild(): 80 | is_rebuild_cache = true 81 | 82 | func on_show(): 83 | if (search_option_btn.selected != 0): 84 | search_option_btn.selected = 0 85 | 86 | is_rebuild_cache = true 87 | 88 | var rebuild_ui: bool = false 89 | var all_tab_not_pressed: bool = filter_bar.current_tab != 0 90 | rebuild_ui = is_rebuild_cache || all_tab_not_pressed 91 | 92 | if (is_rebuild_cache): 93 | rebuild_cache() 94 | 95 | if (rebuild_ui): 96 | if (all_tab_not_pressed): 97 | # Triggers the ui update. 98 | filter_bar.current_tab = 0 99 | else: 100 | fill_files_list() 101 | 102 | filter_txt.select_all() 103 | focus_and_select_first() 104 | 105 | func rebuild_cache(): 106 | is_rebuild_cache = false 107 | 108 | all_files.clear() 109 | scenes.clear() 110 | scripts.clear() 111 | resources.clear() 112 | others.clear() 113 | 114 | build_file_cache() 115 | 116 | func rebuild_cache_and_ui(): 117 | rebuild_cache() 118 | fill_files_list() 119 | 120 | focus_and_select_first() 121 | 122 | func focus_and_select_first(): 123 | filter_txt.grab_focus() 124 | 125 | if (files_list.item_count > 0): 126 | files_list.select(0) 127 | 128 | func build_file_cache(): 129 | var dir: EditorFileSystemDirectory = EditorInterface.get_resource_filesystem().get_filesystem() 130 | build_file_cache_dir(dir) 131 | 132 | all_files.append_array(scenes) 133 | all_files.append_array(scripts) 134 | all_files.append_array(resources) 135 | all_files.append_array(others) 136 | 137 | func build_file_cache_dir(dir: EditorFileSystemDirectory): 138 | for index: int in dir.get_subdir_count(): 139 | build_file_cache_dir(dir.get_subdir(index)) 140 | 141 | for index: int in dir.get_file_count(): 142 | var file: String = dir.get_file_path(index) 143 | if (search_option_btn.get_selected_id() == 0 && file.begins_with(ADDONS)): 144 | continue 145 | 146 | var last_delimiter: int = file.rfind(&"/") 147 | 148 | var file_name: String = file.substr(last_delimiter + 1) 149 | var file_structure: String = &"" 150 | if (file_name.length() + 6 != file.length()): 151 | file_structure = SEPARATOR + STRUCTURE_START + file.substr(6, last_delimiter - 6) + STRUCTURE_END 152 | 153 | var file_data: FileData = FileData.new() 154 | file_data.file = file 155 | file_data.file_name = file_name 156 | file_data.file_name_structure = file_name + file_structure 157 | file_data.file_type = dir.get_file_type(index) 158 | 159 | # Needed, as otherwise we have no icon. 160 | if (file_data.file_type == &"Resource"): 161 | file_data.file_type = &"Object" 162 | 163 | match (file.get_extension()): 164 | &"tscn": scenes.append(file_data) 165 | &"gd": scripts.append(file_data) 166 | &"tres": resources.append(file_data) 167 | &"gdshader": resources.append(file_data) 168 | _: others.append(file_data) 169 | 170 | func change_fill_files_list(): 171 | fill_files_list() 172 | 173 | focus_and_select_first() 174 | 175 | func fill_files_list(): 176 | files_list.clear() 177 | 178 | if (filter_bar.current_tab == 0): 179 | fill_files_list_with(all_files) 180 | elif (filter_bar.current_tab == 1): 181 | fill_files_list_with(scenes) 182 | elif (filter_bar.current_tab == 2): 183 | fill_files_list_with(scripts) 184 | elif (filter_bar.current_tab == 3): 185 | fill_files_list_with(resources) 186 | elif (filter_bar.current_tab == 4): 187 | fill_files_list_with(others) 188 | 189 | func fill_files_list_with(files: Array[FileData]): 190 | var filter_text: String = filter_txt.text 191 | files.sort_custom(sort_by_filter) 192 | 193 | for file_data: FileData in files: 194 | var file: String = file_data.file 195 | if (filter_text.is_empty() || filter_text.is_subsequence_ofn(file)): 196 | var icon: Texture2D = EditorInterface.get_base_control().get_theme_icon(file_data.file_type, &"EditorIcons") 197 | 198 | files_list.add_item(file_data.file_name_structure, icon) 199 | files_list.set_item_metadata(files_list.item_count - 1, file) 200 | files_list.set_item_tooltip(files_list.item_count - 1, file) 201 | 202 | func sort_by_filter(file_data1: FileData, file_data2: FileData) -> bool: 203 | var filter_text: String = filter_txt.text 204 | var name1: String = file_data1.file_name 205 | var name2: String = file_data2.file_name 206 | 207 | for index: int in filter_text.length(): 208 | if (index >= name1.length()): 209 | return true 210 | if (index >= name2.length()): 211 | return false 212 | 213 | var char: String = filter_text[index] 214 | var a_match: bool = char== name1[index] 215 | var b_match: bool = char == name2[index] 216 | 217 | if (a_match && !b_match): 218 | return true 219 | 220 | if (b_match && !a_match): 221 | return false 222 | 223 | return name1 < name2 224 | 225 | class FileData: 226 | var file: String 227 | var file_name: String 228 | var file_name_structure: String 229 | var file_type: StringName 230 | -------------------------------------------------------------------------------- /addons/script-ide/quickopen/quick_open_panel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=14 format=3 uid="uid://d2pttchmj3n7q"] 2 | 3 | [ext_resource type="Script" path="res://addons/script-ide/quickopen/quick_open_panel.gd" id="1_3tl1s"] 4 | 5 | [sub_resource type="Image" id="Image_dfysb"] 6 | data = { 7 | "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 16, 225, 225, 225, 134, 224, 224, 224, 209, 224, 224, 224, 245, 224, 224, 224, 245, 224, 224, 224, 208, 224, 224, 224, 131, 236, 236, 236, 13, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 225, 225, 225, 225, 68, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 183, 224, 224, 224, 198, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 198, 224, 224, 224, 189, 224, 224, 224, 255, 224, 224, 224, 254, 224, 224, 224, 65, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 16, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 120, 226, 226, 226, 60, 224, 224, 224, 255, 225, 225, 225, 109, 225, 225, 225, 110, 224, 224, 224, 255, 226, 226, 226, 60, 224, 224, 224, 128, 224, 224, 224, 255, 225, 225, 225, 223, 234, 234, 234, 12, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 134, 224, 224, 224, 255, 225, 225, 225, 183, 255, 255, 255, 0, 224, 224, 224, 153, 224, 224, 224, 243, 255, 255, 255, 4, 255, 255, 255, 4, 224, 224, 224, 244, 225, 225, 225, 151, 255, 255, 255, 1, 225, 225, 225, 191, 224, 224, 224, 255, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 209, 224, 224, 224, 255, 224, 224, 224, 72, 255, 255, 255, 0, 224, 224, 224, 216, 224, 224, 224, 198, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 199, 224, 224, 224, 214, 255, 255, 255, 0, 226, 226, 226, 78, 224, 224, 224, 255, 224, 224, 224, 206, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 243, 224, 224, 224, 255, 226, 226, 226, 78, 255, 255, 255, 0, 224, 224, 224, 244, 225, 225, 225, 151, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 152, 224, 224, 224, 242, 255, 255, 255, 1, 227, 227, 227, 81, 224, 224, 224, 255, 224, 224, 224, 241, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 245, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 229, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 147, 225, 225, 225, 149, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 230, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 244, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 208, 224, 224, 224, 255, 224, 224, 224, 147, 224, 224, 224, 161, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 235, 224, 224, 224, 235, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 160, 225, 225, 225, 150, 224, 224, 224, 255, 224, 224, 224, 205, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 131, 224, 224, 224, 255, 224, 224, 224, 189, 255, 255, 255, 1, 224, 224, 224, 152, 224, 224, 224, 243, 255, 255, 255, 4, 255, 255, 255, 4, 224, 224, 224, 244, 225, 225, 225, 151, 255, 255, 255, 2, 225, 225, 225, 199, 224, 224, 224, 255, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 0, 236, 236, 236, 13, 224, 224, 224, 225, 224, 224, 224, 255, 224, 224, 224, 128, 225, 225, 225, 67, 224, 224, 224, 255, 225, 225, 225, 110, 226, 226, 226, 111, 224, 224, 224, 255, 225, 225, 225, 67, 225, 225, 225, 135, 224, 224, 224, 255, 224, 224, 224, 221, 234, 234, 234, 12, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 68, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 194, 224, 224, 224, 220, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 219, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 253, 227, 227, 227, 62, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 66, 224, 224, 224, 225, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 220, 226, 226, 226, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 237, 237, 237, 14, 224, 224, 224, 130, 224, 224, 224, 206, 224, 224, 224, 244, 224, 224, 224, 244, 224, 224, 224, 205, 225, 225, 225, 124, 230, 230, 230, 10, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), 8 | "format": "RGBA8", 9 | "height": 16, 10 | "mipmaps": false, 11 | "width": 16 12 | } 13 | 14 | [sub_resource type="ImageTexture" id="ImageTexture_p6ab8"] 15 | image = SubResource("Image_dfysb") 16 | 17 | [sub_resource type="Image" id="Image_blafy"] 18 | data = { 19 | "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 25, 226, 226, 226, 70, 229, 229, 229, 39, 255, 255, 255, 0, 226, 226, 226, 103, 224, 224, 224, 219, 224, 224, 224, 156, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 26, 225, 225, 225, 25, 255, 255, 255, 0, 224, 224, 224, 74, 224, 224, 224, 177, 226, 226, 226, 111, 255, 255, 255, 0, 224, 224, 224, 98, 224, 224, 224, 255, 225, 225, 225, 182, 255, 255, 255, 0, 228, 228, 228, 46, 224, 224, 224, 255, 224, 224, 224, 197, 255, 255, 255, 0, 224, 224, 224, 57, 224, 224, 224, 255, 224, 224, 224, 187, 255, 255, 255, 0, 225, 225, 225, 42, 224, 224, 224, 255, 224, 224, 224, 232, 255, 255, 255, 6, 224, 224, 224, 8, 225, 225, 225, 182, 224, 224, 224, 153, 255, 255, 255, 7, 255, 255, 255, 0, 228, 228, 228, 37, 255, 255, 255, 7, 255, 255, 255, 0, 229, 229, 229, 19, 224, 224, 224, 237, 224, 224, 224, 198, 225, 225, 225, 17, 255, 255, 255, 0, 227, 227, 227, 71, 224, 224, 224, 48, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 228, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 73, 224, 224, 224, 226, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), 20 | "format": "RGBA8", 21 | "height": 16, 22 | "mipmaps": false, 23 | "width": 16 24 | } 25 | 26 | [sub_resource type="ImageTexture" id="ImageTexture_bbwjp"] 27 | image = SubResource("Image_blafy") 28 | 29 | [sub_resource type="Image" id="Image_4wc14"] 30 | data = { 31 | "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 33, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 95, 224, 224, 224, 57, 255, 255, 255, 0, 224, 224, 224, 99, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 93, 255, 255, 255, 0, 224, 224, 224, 57, 224, 224, 224, 90, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 93, 224, 224, 224, 255, 224, 224, 224, 254, 224, 224, 224, 165, 224, 224, 224, 217, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 214, 225, 225, 225, 167, 224, 224, 224, 254, 224, 224, 224, 254, 224, 224, 224, 88, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 228, 228, 228, 55, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 253, 225, 225, 225, 51, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 166, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 160, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 33, 224, 224, 224, 99, 224, 224, 224, 217, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 186, 224, 224, 224, 32, 224, 224, 224, 33, 224, 224, 224, 187, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 98, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 36, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 33, 255, 255, 255, 0, 255, 255, 255, 0, 229, 229, 229, 38, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 31, 226, 226, 226, 95, 224, 224, 224, 216, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 187, 225, 225, 225, 34, 226, 226, 226, 35, 224, 224, 224, 192, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 213, 226, 226, 226, 95, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 166, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 163, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 57, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 254, 227, 227, 227, 54, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 90, 224, 224, 224, 254, 224, 224, 224, 253, 224, 224, 224, 161, 225, 225, 225, 215, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 213, 224, 224, 224, 162, 224, 224, 224, 253, 224, 224, 224, 253, 226, 226, 226, 86, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 88, 225, 225, 225, 51, 255, 255, 255, 0, 224, 224, 224, 98, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 95, 255, 255, 255, 0, 227, 227, 227, 53, 226, 226, 226, 86, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 32, 224, 224, 224, 255, 224, 224, 224, 255, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), 32 | "format": "RGBA8", 33 | "height": 16, 34 | "mipmaps": false, 35 | "width": 16 36 | } 37 | 38 | [sub_resource type="ImageTexture" id="ImageTexture_ghict"] 39 | image = SubResource("Image_4wc14") 40 | 41 | [sub_resource type="Image" id="Image_7rebo"] 42 | data = { 43 | "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 99, 224, 224, 224, 213, 224, 224, 224, 212, 224, 224, 224, 97, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 99, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 220, 224, 224, 224, 97, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 99, 224, 224, 224, 222, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 213, 226, 226, 226, 87, 224, 224, 224, 88, 224, 224, 224, 214, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 220, 224, 224, 224, 97, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 199, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 215, 224, 224, 224, 89, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 90, 224, 224, 224, 216, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 194, 255, 255, 255, 0, 255, 255, 255, 4, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 221, 224, 224, 224, 99, 255, 255, 255, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 5, 225, 225, 225, 101, 225, 225, 225, 223, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 3, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 222, 225, 225, 225, 100, 225, 225, 225, 100, 225, 225, 225, 223, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 3, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 2, 226, 226, 226, 87, 224, 224, 224, 213, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 2, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 2, 226, 226, 226, 87, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 1, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 100, 255, 255, 255, 5, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 196, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 223, 225, 225, 225, 101, 255, 255, 255, 5, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 193, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 225, 225, 225, 93, 224, 224, 224, 218, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 223, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 216, 225, 225, 225, 91, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 225, 225, 225, 93, 224, 224, 224, 218, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 216, 225, 225, 225, 91, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 225, 225, 225, 93, 224, 224, 224, 208, 225, 225, 225, 207, 225, 225, 225, 91, 255, 255, 255, 2, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), 44 | "format": "RGBA8", 45 | "height": 16, 46 | "mipmaps": false, 47 | "width": 16 48 | } 49 | 50 | [sub_resource type="ImageTexture" id="ImageTexture_grjtr"] 51 | image = SubResource("Image_7rebo") 52 | 53 | [sub_resource type="Image" id="Image_e7nb8"] 54 | data = { 55 | "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 181, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 77, 225, 225, 225, 76, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 8, 224, 224, 224, 222, 224, 224, 224, 221, 224, 224, 224, 8, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 120, 224, 224, 224, 32, 224, 224, 224, 128, 224, 224, 224, 255, 224, 224, 224, 255, 225, 225, 225, 127, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 226, 226, 226, 35, 224, 224, 224, 248, 224, 224, 224, 195, 224, 224, 224, 247, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 247, 225, 225, 225, 34, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 224, 224, 224, 180, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 178, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 181, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 180, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), 56 | "format": "RGBA8", 57 | "height": 16, 58 | "mipmaps": false, 59 | "width": 16 60 | } 61 | 62 | [sub_resource type="ImageTexture" id="ImageTexture_xupch"] 63 | image = SubResource("Image_e7nb8") 64 | 65 | [sub_resource type="Image" id="Image_gfvg1"] 66 | data = { 67 | "data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 68, 224, 224, 224, 184, 224, 224, 224, 240, 224, 224, 224, 232, 224, 224, 224, 186, 227, 227, 227, 62, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 129, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 122, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 68, 224, 224, 224, 254, 224, 224, 224, 254, 224, 224, 224, 123, 224, 224, 224, 32, 224, 224, 224, 33, 225, 225, 225, 125, 224, 224, 224, 254, 224, 224, 224, 254, 226, 226, 226, 69, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 184, 224, 224, 224, 255, 224, 224, 224, 123, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 225, 225, 225, 125, 224, 224, 224, 255, 225, 225, 225, 174, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 240, 224, 224, 224, 255, 231, 231, 231, 31, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 35, 224, 224, 224, 255, 224, 224, 224, 233, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 232, 224, 224, 224, 255, 224, 224, 224, 32, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 228, 228, 228, 37, 224, 224, 224, 255, 224, 224, 224, 228, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 186, 224, 224, 224, 255, 224, 224, 224, 123, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 130, 224, 224, 224, 255, 224, 224, 224, 173, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 227, 227, 227, 62, 224, 224, 224, 255, 224, 224, 224, 254, 225, 225, 225, 126, 225, 225, 225, 34, 227, 227, 227, 36, 224, 224, 224, 131, 224, 224, 224, 255, 224, 224, 224, 255, 226, 226, 226, 77, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 224, 224, 224, 122, 224, 224, 224, 254, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 226, 226, 226, 69, 225, 225, 225, 174, 224, 224, 224, 233, 224, 224, 224, 228, 224, 224, 224, 173, 226, 226, 226, 77, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 255, 224, 224, 224, 210, 231, 231, 231, 21, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 224, 224, 224, 210, 224, 224, 224, 227, 225, 225, 225, 34, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 231, 231, 231, 21, 225, 225, 225, 34, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), 68 | "format": "RGBA8", 69 | "height": 16, 70 | "mipmaps": false, 71 | "width": 16 72 | } 73 | 74 | [sub_resource type="ImageTexture" id="ImageTexture_w6vkw"] 75 | image = SubResource("Image_gfvg1") 76 | 77 | [node name="QuickOpenPanel" type="PopupPanel"] 78 | size = Vector2i(624, 100) 79 | visible = true 80 | script = ExtResource("1_3tl1s") 81 | 82 | [node name="PanelContainer" type="PanelContainer" parent="."] 83 | anchors_preset = 15 84 | anchor_right = 1.0 85 | anchor_bottom = 1.0 86 | offset_left = 4.0 87 | offset_top = 4.0 88 | offset_right = -4.0 89 | offset_bottom = -4.0 90 | grow_horizontal = 2 91 | grow_vertical = 2 92 | size_flags_horizontal = 3 93 | size_flags_vertical = 3 94 | 95 | [node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] 96 | layout_mode = 2 97 | theme_override_constants/margin_left = 5 98 | theme_override_constants/margin_top = 5 99 | theme_override_constants/margin_right = 5 100 | theme_override_constants/margin_bottom = 5 101 | 102 | [node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] 103 | layout_mode = 2 104 | theme_override_constants/separation = 5 105 | 106 | [node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] 107 | layout_mode = 2 108 | theme_override_constants/separation = 4 109 | 110 | [node name="FilterBar" type="TabBar" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 111 | unique_name_in_owner = true 112 | layout_mode = 2 113 | current_tab = 0 114 | clip_tabs = false 115 | scrolling_enabled = false 116 | tab_count = 5 117 | tab_0/title = "All" 118 | tab_0/icon = SubResource("ImageTexture_p6ab8") 119 | tab_1/title = "Scene" 120 | tab_1/icon = SubResource("ImageTexture_bbwjp") 121 | tab_2/title = "GDscript" 122 | tab_2/icon = SubResource("ImageTexture_ghict") 123 | tab_3/title = "Resource" 124 | tab_3/icon = SubResource("ImageTexture_grjtr") 125 | tab_4/title = "Other" 126 | tab_4/icon = SubResource("ImageTexture_xupch") 127 | 128 | [node name="SearchOptionBtn" type="OptionButton" parent="PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] 129 | unique_name_in_owner = true 130 | layout_mode = 2 131 | selected = 0 132 | item_count = 2 133 | popup/item_0/text = "Project" 134 | popup/item_0/id = 0 135 | popup/item_1/text = "Project+Addons" 136 | popup/item_1/id = 1 137 | 138 | [node name="FilterTxt" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer"] 139 | unique_name_in_owner = true 140 | layout_mode = 2 141 | placeholder_text = "Filter files" 142 | right_icon = SubResource("ImageTexture_w6vkw") 143 | 144 | [node name="FilesList" type="ItemList" parent="PanelContainer/MarginContainer/VBoxContainer"] 145 | unique_name_in_owner = true 146 | layout_mode = 2 147 | size_flags_vertical = 3 148 | allow_reselect = true 149 | --------------------------------------------------------------------------------