diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 654a13b..25ad962 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,51 +15,67 @@ jobs: config: - { id: "aspect_ratio_resize_container", + id-name: "aspect_ratio_resize_container", name: "AspectRatioResizeContainer", asset-id: "2089", } - { id: "custom_theme_overrides", + id-name: "custom_theme_overrides", name: "Custom Theme Overrides", asset-id: "2091", } - { id: "git_sha_project_setting", + id-name: "git_sha_project_setting", name: "Git SHA Project Setting", asset-id: "1979", } - { id: "glogging", + id-name: "glogging", name: "GLogging", asset-id: "no-deploy", } - { id: "hide_private_properties", + id-name: "hide_private_properties", name: "Hide Private Properties", asset-id: "1989", } - { id: "icon_explorer", + id-name: "icon_explorer", name: "Icon Explorer", asset-id: "2511", } - { id: "icons_patcher", + id-name: "icons_patcher", name: "Icons Patcher", asset-id: "1980", } - { id: "licenses", + id-name: "licenses", name: "License Manager", asset-id: "1969", } + - { + id: "kenyoni/plugin_reloader", + id-name: "plugin_reloader", + name: "Plugin Reloader", + asset-id: "no-deploy", + } - { id: "qr_code", + id-name: "qr_code", name: "QR Code", asset-id: "2090", } - { id: "texture_button_colored", + id-name: "texture_button_colored", name: "TextureButtonColored", asset-id: "2092", } @@ -92,20 +108,20 @@ jobs: uses: actions/upload-artifact@v3 with: name: ${{ matrix.config.name }} - path: archives/${{ matrix.config.id }}-*.zip + path: archives/${{ matrix.config.id-name }}-*.zip - uses: mukunku/tag-exists-action@v1.2.0 id: checkTag with: - tag: ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} + tag: ${{ matrix.config.id-name }}-${{ steps.prepare-artifacts.outputs.version }} - name: Prepare Release if: ${{ steps.checkTag.outputs.exists == 'false' }} run: | git config user.name github-actions git config user.email github-actions@github.com - git tag ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} - git push origin tag ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} + git tag ${{ matrix.config.id-name }}-${{ steps.prepare-artifacts.outputs.version }} + git push origin tag ${{ matrix.config.id-name }}-${{ steps.prepare-artifacts.outputs.version }} # wait 5s that the pushed tag is available in the next step, sometimes the next step saw only the local tag - name: Wait 5s @@ -117,7 +133,7 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | - gh release create ${{ matrix.config.id }}-${{ steps.prepare-artifacts.outputs.version }} ./archives/* --title "${{ matrix.config.name }} ${{ steps.prepare-artifacts.outputs.version }}" --notes "${{ steps.prepare-artifacts.outputs.notes }}" + gh release create ${{ matrix.config.id-name }}-${{ steps.prepare-artifacts.outputs.version }} ./archives/* --title "${{ matrix.config.name }} ${{ steps.prepare-artifacts.outputs.version }}" --notes "${{ steps.prepare-artifacts.outputs.notes }}" #- name: Deploy to Godot Asset Library # if: ${{ matrix.config.asset-id != 'no-deploy' }} diff --git a/README.md b/README.md index 4c62d48..7f5363e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,8 @@ If you import any plugin or open a Godot project for the first time, the plugins Manage license and copyright for third party graphics, software or libraries. - [Logging](https://kenyoni-software.github.io/godot-addons/addons/glogging) Simple logger. +- [Plugin Reloader](https://kenyoni-software.github.io/godot-addons/addons/plugin_reloader) + Enable or disable plugins from within the editor main screen. - [QR Code](https://kenyoni-software.github.io/godot-addons/addons/qr_code) QRCodeRect and QR Code generation. - [TextureButtonColored](https://kenyoni-software.github.io/godot-addons/addons/texture_button_colored) diff --git a/addons/kenyoni/plugin_reloader/internal/reloader.gd b/addons/kenyoni/plugin_reloader/internal/reloader.gd new file mode 100644 index 0000000..9b67310 --- /dev/null +++ b/addons/kenyoni/plugin_reloader/internal/reloader.gd @@ -0,0 +1,61 @@ +@tool +extends MarginContainer + +@export var _reload_button: CheckButton +@export var _option_button: OptionButton + +var _last_selection: String = "" + +func _ready() -> void: + self._option_button.item_selected.connect(func(idx: int) -> void: + self._last_selection = self._option_button.get_item_metadata(idx) + self._update_button_bar() + ) + self._reload_button.toggled.connect(func(toggled: bool) -> void: + EditorInterface.set_plugin_enabled("res://addons/" + self._last_selection + "/plugin.cfg", toggled) + self._reload_plugin_list() + ) + + EditorInterface.get_resource_filesystem().filesystem_changed.connect(self._reload_plugin_list) + self._reload_plugin_list() + +func _update_button_bar() -> void: + if self._last_selection != "": + self._reload_button.set_pressed_no_signal(EditorInterface.is_plugin_enabled("res://addons/" + self._last_selection + "/plugin.cfg")) + self._option_button.icon = null + self._reload_button.disabled = self._last_selection == "" + +func _reload_plugin_list() -> void: + self._option_button.clear() + for dir: String in DirAccess.get_directories_at("res://addons/"): + self._add_plugin_to_list(dir) + # subfolder + for sub_dir: String in DirAccess.get_directories_at("res://addons/" + dir): + self._add_plugin_to_list(dir + "/" + sub_dir) + + if self._last_selection == "" && self._option_button.get_item_count() > 0: + self._last_selection = self._option_button.get_item_metadata(0) + self._update_button_bar() + +func _add_plugin_to_list(plugin_id: String) -> void: + # ignore the current plugin + if plugin_id == "kenyoni/plugin_reloader": + return + + var cfg_path: String = "res://addons/" + plugin_id + "/plugin.cfg" + if !FileAccess.file_exists(cfg_path): + return + + var plugin_cfg: ConfigFile = ConfigFile.new() + plugin_cfg.load(cfg_path) + var plugin_name: String = plugin_cfg.get_value("plugin", "name", plugin_id) + self._option_button.add_item(plugin_name) + var idx: int = self._option_button.get_item_count() - 1 + self._option_button.set_item_metadata(idx, plugin_id) + self._option_button.set_item_tooltip(idx, "res://addons/" + plugin_id + "/") + if EditorInterface.is_plugin_enabled(cfg_path): + self._option_button.set_item_icon(idx, self.get_theme_icon(&"TileChecked", &"EditorIcons")) + else: + self._option_button.set_item_icon(idx, self.get_theme_icon(&"TileUnchecked", &"EditorIcons")) + if plugin_id == self._last_selection: + self._option_button.select(idx) diff --git a/addons/kenyoni/plugin_reloader/internal/reloader.tscn b/addons/kenyoni/plugin_reloader/internal/reloader.tscn new file mode 100644 index 0000000..44ebfbe --- /dev/null +++ b/addons/kenyoni/plugin_reloader/internal/reloader.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=2 format=3 uid="uid://dryoyuj4vl0l5"] + +[ext_resource type="Script" path="res://addons/kenyoni/plugin_reloader/internal/reloader.gd" id="1_kd7gg"] + +[node name="Reloader" type="MarginContainer" node_paths=PackedStringArray("_reload_button", "_option_button")] +offset_right = 76.0 +offset_bottom = 24.0 +script = ExtResource("1_kd7gg") +_reload_button = NodePath("HBoxContainer/reload_button") +_option_button = NodePath("HBoxContainer/OptionButton") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="reload_button" type="CheckButton" parent="HBoxContainer"] +layout_mode = 2 + +[node name="OptionButton" type="OptionButton" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 diff --git a/addons/kenyoni/plugin_reloader/plugin.cfg b/addons/kenyoni/plugin_reloader/plugin.cfg new file mode 100644 index 0000000..78c4c44 --- /dev/null +++ b/addons/kenyoni/plugin_reloader/plugin.cfg @@ -0,0 +1,19 @@ +[plugin] + +name="Plugin Reloader" +description="Quickly reload plugins from the editor." +author="Kenyoni Software" +version="1.0.0" +script="plugin.gd" +license="MIT" +repository="https://github.com/kenyoni-software/godot-addons" +keywords=[ + "tool" +] +classifiers=[ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: MIT License" +] + +[plugin.dependencies] +godot=">=4.2" diff --git a/addons/kenyoni/plugin_reloader/plugin.gd b/addons/kenyoni/plugin_reloader/plugin.gd new file mode 100644 index 0000000..94fe541 --- /dev/null +++ b/addons/kenyoni/plugin_reloader/plugin.gd @@ -0,0 +1,20 @@ +@tool +extends EditorPlugin + +const ReloaderScene: PackedScene = preload("res://addons/kenyoni/plugin_reloader/internal/reloader.tscn") +const Reloader := preload("res://addons/kenyoni/plugin_reloader/internal/reloader.gd") + +var _reloader: Reloader + +func _get_plugin_name() -> String: + return "Plugin Reloader" + +func _enter_tree() -> void: + self._reloader = ReloaderScene.instantiate() + self.add_control_to_container(CustomControlContainer.CONTAINER_TOOLBAR, self._reloader) + # move before editor run bar + self._reloader.get_parent().move_child(self._reloader, self._reloader.get_parent().find_child("@EditorRunBar@*", true, false).get_index()) + +func _exit_tree() -> void: + self.remove_control_from_container(CustomControlContainer.CONTAINER_TOOLBAR, self._reloader) + self._reloader.queue_free() diff --git a/doc/docs/addons/plugin_reloader.md b/doc/docs/addons/plugin_reloader.md new file mode 100644 index 0000000..f6f82ca --- /dev/null +++ b/doc/docs/addons/plugin_reloader.md @@ -0,0 +1,20 @@ +# Plugin Reloader + +Enable or disable plugins from within the editor main screen. + +## Compatibility + +| Godot | Version | +|-------|----------| +| 4.3 | >= 1.0.0 | +| 4.2 | >= 1.0.0 | + +## Screenshot + +![plugin reloader screenshot](plugin_reloader/plugin_reloader.png "Plugin Reloader") + +## Changelog + +### 1.0.0 + +- Initial release diff --git a/doc/docs/addons/plugin_reloader/plugin_reloader.png b/doc/docs/addons/plugin_reloader/plugin_reloader.png new file mode 100644 index 0000000..6a92d5c Binary files /dev/null and b/doc/docs/addons/plugin_reloader/plugin_reloader.png differ diff --git a/project.godot b/project.godot index dbcfda0..eb4c1d4 100644 --- a/project.godot +++ b/project.godot @@ -29,7 +29,7 @@ gdscript/warnings/unsafe_call_argument=1 [editor_plugins] -enabled=PackedStringArray("res://addons/aspect_ratio_resize_container/plugin.cfg", "res://addons/custom_theme_overrides/plugin.cfg", "res://addons/explore-editor-theme/plugin.cfg", "res://addons/git_sha_project_setting/plugin.cfg", "res://addons/glogging/plugin.cfg", "res://addons/hide_private_properties/plugin.cfg", "res://addons/icon_explorer/plugin.cfg", "res://addons/icons_patcher/plugin.cfg", "res://addons/licenses/plugin.cfg", "res://addons/qr_code/plugin.cfg", "res://addons/texture_button_colored/plugin.cfg") +enabled=PackedStringArray("res://addons/aspect_ratio_resize_container/plugin.cfg", "res://addons/custom_theme_overrides/plugin.cfg", "res://addons/git_sha_project_setting/plugin.cfg", "res://addons/glogging/plugin.cfg", "res://addons/hide_private_properties/plugin.cfg", "res://addons/icon_explorer/plugin.cfg", "res://addons/icons_patcher/plugin.cfg", "res://addons/kenyoni/plugin_reloader/plugin.cfg", "res://addons/licenses/plugin.cfg", "res://addons/qr_code/plugin.cfg", "res://addons/texture_button_colored/plugin.cfg") [plugins] diff --git a/publisher/cli.go b/publisher/cli.go index b5fd0de..d6d6938 100644 --- a/publisher/cli.go +++ b/publisher/cli.go @@ -181,7 +181,7 @@ func doActionAssetLibrary(baseDir string, addonId string, cfg assetLibraryAction assetData := internal.AssetData{ AssetId: cfg.AssetId, Title: plgCfg.Plugin.Name, - Description: fmt.Sprintf("%s\n\n%s", plgCfg.Plugin.Description, fmt.Sprintf("More detailed information and documentation is available at https://kenyoni-software.github.io/godot-addons/addons/%s", addon.Id())), + Description: fmt.Sprintf("%s\n\n%s", plgCfg.Plugin.Description, fmt.Sprintf("More detailed information and documentation is available at https://kenyoni-software.github.io/godot-addons/addons/%s", addon.IdName())), VersionString: plgCfg.Plugin.Version, GodotVersion: gdMinversion, CategoryId: cfg.Category, @@ -222,7 +222,7 @@ func doActionZip(baseDir string, addonId string, cfg zipActionCfg) { if outputDir == "" { outputDir = filepath.Join(addon.ProjectPath(), "archives") } - outputFile := filepath.Join(outputDir, addon.Id()+"-"+strings.ReplaceAll(plgCfg.Plugin.Version, ".", "_")+".zip") + outputFile := filepath.Join(outputDir, addon.IdName()+"-"+strings.ReplaceAll(plgCfg.Plugin.Version, ".", "_")+".zip") err = addon.Zip(outputFile) if err != nil { log.Fatalln(err) diff --git a/publisher/internal/addon.go b/publisher/internal/addon.go index 0e6daa3..cc67ef0 100644 --- a/publisher/internal/addon.go +++ b/publisher/internal/addon.go @@ -5,6 +5,7 @@ import ( "log" "os" "path/filepath" + "strings" "github.com/pelletier/go-toml/v2" ) @@ -39,10 +40,17 @@ func NewAddon(id string, projectPath string) *Addon { } } +// Id contains the namespace directory and the addon directory name like "kenyoni/addon_name" func (addon *Addon) Id() string { return addon.addonId } +// IdName is only the addon directory name like "addon_name" +func (addon *Addon) IdName() string { + splitted := strings.Split(addon.addonId, "/") + return splitted[len(splitted)-1] +} + func (addon *Addon) ProjectPath() string { return addon.projectPath } @@ -74,7 +82,7 @@ func (addon *Addon) Zip(outputFile string) error { if err != nil { return err } - exampleDir := filepath.Join(addon.ProjectPath(), "examples", addon.Id()) + exampleDir := filepath.Join(addon.ProjectPath(), "examples", addon.IdName()) // zip example directory only if it exists if _, err := os.Stat(exampleDir); err == nil { err = ZipDir(zw, exampleDir, filepath.Join("examples", addon.Id()))