Repository: godot-extended-libraries/godot-plugin-refresher Branch: master Commit: e5007ae892de Files: 14 Total size: 20.8 KB Directory structure: gitextract_17kfx187/ ├── .gitattributes ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── static_checks.yml ├── .gitignore ├── LICENSE ├── README.md ├── addons/ │ └── godot-plugin-refresher/ │ ├── LICENSE │ ├── plugin.cfg │ ├── plugin_refresher.gd │ ├── plugin_refresher.tscn │ └── plugin_refresher_plugin.gd ├── file_format.sh ├── icon.png.import └── project.godot ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Normalize EOL for all files that Git considers text files. * text=auto eol=lf # Ignore everything outside of the addons folder when exporting. /.github export-ignore /.gitattributes export-ignore /.gitignore export-ignore /file_format.sh export-ignore /icon.png export-ignore /icon.png.import export-ignore /LICENSE.md export-ignore /project.godot export-ignore /README.md export-ignore ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/workflows/static_checks.yml ================================================ name: 📊 Static Checks on: [push, pull_request] jobs: format: name: File formatting (file_format.sh) runs-on: ubuntu-20.04 steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update -qq sudo apt-get install -qq dos2unix recode - name: File formatting checks (file_format.sh) run: | bash ./file_format.sh ================================================ FILE: .gitignore ================================================ # Godot 4+ specific ignores .godot/ # Godot-specific ignores .import/ export.cfg export_presets.cfg # Imported translations (automatically generated from CSV files) *.translation # Mono-specific ignores .mono/ data_*/ mono_crash.*.json # System/tool-specific ignores .directory *~ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018-2021 Will Nations Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # godot-plugin-refresher This plugin simplifies plugin development for those wanting to make tools for the Godot Editor. > The `master` branch supports Godot 4.2+. For Godot 4.0-4.1, see the `4.0` branch. For Godot 3, see the `3.x` branch. The plugin adds a dropdown and refresh button to the main toolbar in the top-right corner of the Godot Editor. *Other* plugins' names will show up in the dropdown. Clicking the refresh button will toggle the plugin off and then back on. Disabled plugins still appear in the list & be toggleable (effectively just enabling them), but will ask to confirm beforehand. Here's an example of working on a WIP internal "GodotTools" plugin amidst an in-development project: ![example of dropdown](./example_dropdown.png) This makes it much easier to iterate on a single plugin since rather than having to... 1. Click Project Settings. 2. Go to Plugins tab (first time). 3. Find the desired plugin. 4. Click the dropdown. 5. Select the opposite option. 6. Click the dropdown again. 7. Click the original option. 8. Close the Project Settings. You instead just... 1. Click the dropdown (first time). 2. Select your WIP plugin (first time). 3. Click the refresh button. Please consider starring the repo if you like the project and let me know if you have any feedback for bugs / feature improvements in the Issues. If you'd like to support my work, please [send tips to my Kofi](https://ko-fi.com/willnationsdev). Cheers! ================================================ FILE: addons/godot-plugin-refresher/LICENSE ================================================ MIT License Copyright (c) 2018-2021 Will Nations Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: addons/godot-plugin-refresher/plugin.cfg ================================================ [plugin] name="Godot Plugin Refresher" description="A toolbar addition to facilitate toggling off/on a selected plugin. Updated for Godot 4.3" author="willnationsdev" version="1.2" script="plugin_refresher_plugin.gd" ================================================ FILE: addons/godot-plugin-refresher/plugin_refresher.gd ================================================ @tool extends HBoxContainer signal request_refresh_plugin(p_name: String) signal confirm_refresh_plugin(p_name: String) @onready var options: OptionButton = $OptionButton func _ready() -> void: if get_tree().edited_scene_root == self: return # This is the scene opened in the editor! $RefreshButton.icon = EditorInterface.get_editor_theme().get_icon("Reload", "EditorIcons") func update_items(p_plugins_info: Array) -> void: if not options: return options.clear() var plugins := p_plugins_info[0] as Dictionary var display_names_map := p_plugins_info[1] as Dictionary var plugin_dirs: Array[String] = [] plugin_dirs.assign(plugins.keys()) for idx in plugin_dirs.size(): var plugin_dirname := plugin_dirs[idx] var plugin_data = plugins[plugin_dirname] # Array[String] used as a Tuple. var plugin_name := plugin_data[0] as String var plugin_path := plugin_data[1] as String var display_name := display_names_map[plugin_path] as String options.add_item(display_name, idx) options.set_item_metadata(idx, plugin_path) # Note: For whatever reason, statically typing `p_name` inexplicably causes # an error about converting from Nil to String, even if the value is converted. func select_plugin(p_name) -> void: if not options or not p_name: return for idx in options.get_item_count(): var plugin := str(options.get_item_metadata(idx)) if plugin == str(p_name): options.selected = options.get_item_id(idx) break func _on_RefreshButton_pressed() -> void: if options.selected == -1: return # nothing selected var plugin := str(options.get_item_metadata(options.selected)) if not plugin: return emit_signal("request_refresh_plugin", plugin) func show_warning(p_name: String) -> void: $ConfirmationDialog.dialog_text = ( """ Plugin `%s` is currently disabled.\n Do you want to enable it now? """ % [p_name] ) $ConfirmationDialog.popup_centered() func _on_ConfirmationDialog_confirmed() -> void: var plugin := options.get_item_metadata(options.selected) as String emit_signal("confirm_refresh_plugin", plugin) ================================================ FILE: addons/godot-plugin-refresher/plugin_refresher.tscn ================================================ [gd_scene load_steps=2 format=3 uid="uid://dnladpgp5dwts"] [ext_resource type="Script" path="res://addons/godot-plugin-refresher/plugin_refresher.gd" id="1"] [node name="HBoxContainer" type="HBoxContainer"] script = ExtResource("1") [node name="VSeparator" type="VSeparator" parent="."] layout_mode = 2 [node name="OptionButton" type="OptionButton" parent="."] layout_mode = 2 [node name="RefreshButton" type="Button" parent="."] layout_mode = 2 [node name="ConfirmationDialog" type="ConfirmationDialog" parent="."] dialog_autowrap = true [connection signal="pressed" from="RefreshButton" to="." method="_on_RefreshButton_pressed"] [connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_ConfirmationDialog_confirmed"] ================================================ FILE: addons/godot-plugin-refresher/plugin_refresher_plugin.gd ================================================ @tool extends EditorPlugin const ADDONS_PATH := "res://addons/" const PLUGIN_CONFIG_DIR := "plugins/plugin_refresher" const PLUGIN_CONFIG := "settings.cfg" const PLUGIN_NAME := "Godot Plugin Refresher" const SETTINGS := "settings" const SETTING_RECENT := "recently_used" const Refresher := preload("plugin_refresher.gd") var plugin_config := ConfigFile.new() var refresher: Refresher = null func _enter_tree() -> void: refresher = preload("plugin_refresher.tscn").instantiate() as Refresher add_control_to_container(CONTAINER_TOOLBAR, refresher) # Watch whether any plugin is changed, added or removed on the filesystem var efs := EditorInterface.get_resource_filesystem() efs.filesystem_changed.connect(_on_filesystem_changed) refresher.request_refresh_plugin.connect(_on_request_refresh_plugin) refresher.confirm_refresh_plugin.connect(_on_confirm_refresh_plugin) _reload_plugins_list() _load_settings() func _exit_tree() -> void: remove_control_from_container(CONTAINER_TOOLBAR, refresher) refresher.free() func _reload_plugins_list() -> void: var cfg_paths: Array[String] = [] var plugins := {} var display_names_map := {} # full path to display name find_cfgs(ADDONS_PATH, cfg_paths) for cfg_path in cfg_paths: var plugin_cfg := ConfigFile.new() var err := plugin_cfg.load(cfg_path) if err: push_error("ERROR LOADING PLUGIN FILE: %s" % err) else: var plugin_name := plugin_cfg.get_value("plugin", "name") if plugin_name != PLUGIN_NAME: var addon_dir_name = cfg_path.split("addons/")[-1].split("/plugin.cfg")[0] plugins[addon_dir_name] = [plugin_name, cfg_path] # This will be an array of the addon/* directory names. var plugin_dirs: Array[String] = [] plugin_dirs.assign(plugins.keys()) # typed array "casting" var plugin_names: Array[String] = [] plugin_names.assign(plugin_dirs.map(func(k): return plugins[k][0])) for plugin_dirname in plugin_dirs: var plugin_name = plugins[plugin_dirname][0] var display_name = plugin_name if plugin_names.count(plugin_name) == 1 else "%s (%s)" % [plugin_name, plugin_dirname] display_names_map[plugins[plugin_dirname][1]] = display_name refresher.update_items([plugins, display_names_map]) func find_cfgs(dir_path: String, cfgs: Array): var dir := DirAccess.open(dir_path) var cfg_path := dir_path.path_join("plugin.cfg") if dir.file_exists(cfg_path): cfgs.append(cfg_path) return if dir: dir.list_dir_begin() var file_name := dir.get_next() while file_name != "": if dir.current_is_dir(): find_cfgs(dir_path.path_join(file_name), cfgs) file_name = dir.get_next() func _load_settings() -> void: var path := get_settings_path() if not FileAccess.file_exists(path): # Create new if running for the first time var config := ConfigFile.new() DirAccess.make_dir_recursive_absolute(path.get_base_dir()) config.save(path) else: plugin_config.load(path) func _save_settings() -> void: plugin_config.save(get_settings_path()) func get_settings_path() -> String: var editor_paths := EditorInterface.get_editor_paths() var dir := editor_paths.get_project_settings_dir() var home := dir.path_join(PLUGIN_CONFIG_DIR) var path := home.path_join(PLUGIN_CONFIG) return path func _on_filesystem_changed() -> void: if refresher: _reload_plugins_list() var recent = get_recent_plugin() if recent: refresher.select_plugin(recent) func get_recent_plugin() -> String: if not plugin_config.has_section_key(SETTINGS, SETTING_RECENT): return "" # not saved yet var recent = str(plugin_config.get_value(SETTINGS, SETTING_RECENT)) return recent func _on_request_refresh_plugin(p_path: String) -> void: assert(not p_path.is_empty()) var disabled := not EditorInterface.is_plugin_enabled(p_path) if disabled: refresher.show_warning(p_path) else: refresh_plugin(p_path) func _on_confirm_refresh_plugin(p_path: String) -> void: refresh_plugin(p_path) func get_plugin_path() -> String: return get_script().resource_path.get_base_dir() func refresh_plugin(p_path: String) -> void: print("Refreshing plugin: ", p_path) var enabled := EditorInterface.is_plugin_enabled(p_path) if enabled: # can only disable an active plugin EditorInterface.set_plugin_enabled(p_path, false) EditorInterface.set_plugin_enabled(p_path, true) plugin_config.set_value(SETTINGS, SETTING_RECENT, p_path) _save_settings() ================================================ FILE: file_format.sh ================================================ #!/usr/bin/env bash # This script ensures proper POSIX text file formatting and a few other things. set -uo pipefail IFS=$'\n\t' # Loops through all text files tracked by Git. git grep -zIl '' | while IFS= read -rd '' f; do # Ensure that files are UTF-8 formatted. recode UTF-8 "$f" 2> /dev/null # Ensure that files have LF line endings and do not contain a BOM. dos2unix "$f" 2> /dev/null # Remove trailing space characters and ensures that files end # with newline characters. -l option handles newlines conveniently. perl -i -ple 's/\s*$//g' "$f" # Remove the character sequence "== true" if it has a leading space. perl -i -pe 's/\x20== true//g' "$f" done git diff > patch.patch FILESIZE="$(stat -c%s patch.patch)" MAXSIZE=5 # If no patch has been generated all is OK, clean up, and exit. if (( FILESIZE < MAXSIZE )); then printf "Files in this commit comply with the formatting rules.\n" rm -f patch.patch exit 0 fi # A patch has been created, notify the user, clean up, and exit. printf "\n*** The following differences were found between the code " printf "and the formatting rules:\n\n" cat patch.patch printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i '\n" rm -f patch.patch exit 1 ================================================ FILE: icon.png.import ================================================ [remap] importer="texture" type="CompressedTexture2D" uid="uid://c1hfjj7vmjkda" path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex" metadata={ "vram_texture": false } [deps] source_file="res://icon.png" dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"] [params] compress/mode=0 compress/high_quality=false compress/lossy_quality=0.7 compress/hdr_compression=1 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false mipmaps/limit=-1 roughness/mode=0 roughness/src_normal="" process/fix_alpha_border=true process/premult_alpha=false process/normal_map_invert_y=false process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 ================================================ FILE: project.godot ================================================ ; Engine configuration file. ; It's best edited using the editor UI and not directly, ; since the parameters that go here are not all obvious. ; ; Format: ; [section] ; section goes between [] ; param=value ; assign values to parameters config_version=5 [application] config/name="Godot Plugin Refresher" run/main_scene="res://addons/godot-plugin-refresher/plugin_refresher.tscn" config/features=PackedStringArray("4.3") config/icon="res://icon.png" [dotnet] project/assembly_name="Godot Plugin Refresher" [editor_plugins] enabled=PackedStringArray("res://addons/godot-plugin-refresher/plugin.cfg") [input] ui_accept={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) ] } ui_select={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":3,"pressure":0.0,"pressed":false,"script":null) ] } ui_cancel={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":false,"script":null) ] } ui_focus_next={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ui_focus_prev={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ui_left={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) ] } ui_right={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null) ] } ui_up={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) ] } ui_down={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) ] } ui_page_up={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ui_page_down={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ui_home={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } ui_end={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } [rendering] quality/driver/driver_name="GLES2" vram_compression/import_etc=true vram_compression/import_etc2=false