From 6721f226f31aec4b3560b388c10924f4774f1415 Mon Sep 17 00:00:00 2001 From: Gregory-Michael Azubuike <74624111+POWERHACK69@users.noreply.github.com> Date: Sat, 4 Jul 2026 03:39:20 +0100 Subject: [PATCH 1/6] feat: add RSS feed tab showing YouTube and blog updates Add an RSS feed feature that displays updates from: - Red Indie Games (YouTube) - GDQuest (YouTube) - Godot Engine (YouTube) - Godot Blog (RSS) Components: - RssFeedFeedItem class for data model - RssFeedControl main scene with fetch/parse logic - RssFeedList extending VBoxList with search/filter - RssFeedItemControl for individual feed items - "Feed" tab added to the title bar with ExternalLink icon Supports both YouTube Atom and standard RSS feed formats. Items sorted by published date (newest first). --- src/components/rss_feed/list/rss_feed_item.gd | 53 ++++++ .../rss_feed/list/rss_feed_item.tscn | 51 ++++++ src/components/rss_feed/list/rss_feed_list.gd | 13 ++ .../rss_feed/list/rss_feed_list.tscn | 17 ++ src/components/rss_feed/rss_feed.gd | 162 ++++++++++++++++++ src/components/rss_feed/rss_feed.tscn | 27 +++ src/components/rss_feed/rss_feed_class.gd | 10 ++ src/main/gui/gui_main.gd | 9 +- src/main/gui/gui_main.tscn | 10 +- 9 files changed, 347 insertions(+), 5 deletions(-) create mode 100644 src/components/rss_feed/list/rss_feed_item.gd create mode 100644 src/components/rss_feed/list/rss_feed_item.tscn create mode 100644 src/components/rss_feed/list/rss_feed_list.gd create mode 100644 src/components/rss_feed/list/rss_feed_list.tscn create mode 100644 src/components/rss_feed/rss_feed.gd create mode 100644 src/components/rss_feed/rss_feed.tscn create mode 100644 src/components/rss_feed/rss_feed_class.gd diff --git a/src/components/rss_feed/list/rss_feed_item.gd b/src/components/rss_feed/list/rss_feed_item.gd new file mode 100644 index 00000000..1af07840 --- /dev/null +++ b/src/components/rss_feed/list/rss_feed_item.gd @@ -0,0 +1,53 @@ +class_name RssFeedItemControl +extends HBoxListItem + +signal tag_clicked(tag: String) + +@onready var _title_label := %TitleLabel as Label +@onready var _source_label := %SourceLabel as Label +@onready var _date_label := %DateLabel as Label +@onready var _open_button := %OpenButton as Button + +var _item: RssFeed.FeedItem +var _tags: Array = [] + + +func init(item: RssFeed.FeedItem) -> void: + _item = item + _tags = [item.source_name] + + _title_label.text = item.title + _source_label.text = item.source_name + _date_label.text = _format_date(item.published) + + _open_button.icon = get_theme_icon("ExternalLink", "EditorIcons") + _open_button.pressed.connect(func() -> void: OS.shell_open(item.link)) + + +func apply_filter(filter: Callable) -> bool: + return filter.call({ + 'name': _item.title, + 'path': _item.source_name, + 'tags': _tags + }) + + +func get_sort_data() -> Dictionary: + return {'ref': self, 'published': _item.published} + + +func _format_date(date_str: String) -> String: + if date_str.is_empty(): + return "" + # ISO 8601: 2026-03-10T14:30:00+00:00 + if "T" in date_str: + var parts := date_str.split("T") + var date_parts := parts[0].split("-") + if date_parts.size() >= 3: + return "%s-%s-%s" % [date_parts[0], date_parts[1], date_parts[2]] + # RFC 2822: Mon, 10 Mar 2026 14:30:00 +0000 + if "," in date_str: + var comma_parts := date_str.split(",") + if comma_parts.size() > 1: + return comma_parts[1].strip_edges().left(16) + return date_str diff --git a/src/components/rss_feed/list/rss_feed_item.tscn b/src/components/rss_feed/list/rss_feed_item.tscn new file mode 100644 index 00000000..0ab8de80 --- /dev/null +++ b/src/components/rss_feed/list/rss_feed_item.tscn @@ -0,0 +1,51 @@ +[gd_scene load_steps=5 format=3] + +[ext_resource type="Script" path="res://src/components/rss_feed/list/rss_feed_item.gd" id="1_script"] +[ext_resource type="Script" path="res://src/components/misc/list_item_title_label.gd" id="2_title"] +[ext_resource type="Script" path="res://src/components/misc/themed_button.gd" id="3_button"] + +[node name="RssFeedItem" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_script") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="TitleLabel" type="Label" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Video Title" +script = ExtResource("2_title") + +[node name="Info" type="HBoxContainer" parent="VBoxContainer"] +modulate = Color(1, 1, 1, 0.6) +layout_mode = 2 + +[node name="SourceLabel" type="Label" parent="VBoxContainer/Info"] +unique_name_in_owner = true +layout_mode = 2 +text = "Source" + +[node name="Spacer" type="Control" parent="VBoxContainer/Info"] +custom_minimum_size = Vector2(10, 0) +layout_mode = 2 + +[node name="DateLabel" type="Label" parent="VBoxContainer/Info"] +unique_name_in_owner = true +layout_mode = 2 +text = "2026-01-01" + +[node name="Spacer2" type="Control" parent="VBoxContainer/Info"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="OpenButton" type="Button" parent="VBoxContainer/Info"] +unique_name_in_owner = true +layout_mode = 2 +flat = true diff --git a/src/components/rss_feed/list/rss_feed_list.gd b/src/components/rss_feed/list/rss_feed_list.gd new file mode 100644 index 00000000..e9be614b --- /dev/null +++ b/src/components/rss_feed/list/rss_feed_list.gd @@ -0,0 +1,13 @@ +extends VBoxList + + +func _post_add(_item_data: Object, _raw_item_control: Control) -> void: + pass + + +func _item_comparator(a: Dictionary, b: Dictionary) -> bool: + return true + + +func _fill_sort_options(_btn: OptionButton) -> void: + pass diff --git a/src/components/rss_feed/list/rss_feed_list.tscn b/src/components/rss_feed/list/rss_feed_list.tscn new file mode 100644 index 00000000..e5a2cc05 --- /dev/null +++ b/src/components/rss_feed/list/rss_feed_list.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="PackedScene" path="res://src/components/v_box_list/v_box_list.tscn" id="1_base"] +[ext_resource type="Script" path="res://src/components/rss_feed/list/rss_feed_list.gd" id="2_script"] +[ext_resource type="PackedScene" path="res://src/components/rss_feed/list/rss_feed_item.tscn" id="3_item"] + +[node name="RssFeedList" instance=ExtResource("1_base")] +script = ExtResource("2_script") +_item_scene = ExtResource("3_item") +_cached_search_key = "rss_feed_list" + +[node name="SearchBox" parent="HBoxContainer" index="1"] +placeholder_text = "Filter Feed" +clear_button_enabled = true + +[node name="HBoxContainer" parent="HBoxContainer" index="2"] +visible = false diff --git a/src/components/rss_feed/rss_feed.gd b/src/components/rss_feed/rss_feed.gd new file mode 100644 index 00000000..7fbcaf5b --- /dev/null +++ b/src/components/rss_feed/rss_feed.gd @@ -0,0 +1,162 @@ +class_name RssFeedControl +extends HBoxContainer + +const HOUR = 60 * 60 +const CACHE_LIFETIME_SEC = 6 * HOUR + +const FEED_SOURCES: Array[Dictionary] = [ + { + "name": "Red Indie Games", + "url": "https://www.youtube.com/feeds/videos.xml?channel_id=UCN5oRj4nn1qVRzOadVc97MQ", + }, + { + "name": "GDQuest", + "url": "https://www.youtube.com/feeds/videos.xml?channel_id=UCxboW7x0jZqFdvMdCFKTMsQ", + }, + { + "name": "Godot Engine", + "url": "https://www.youtube.com/feeds/videos.xml?channel_id=UCKIDvfZD1ZhY4_hhbotf7wA", + }, + { + "name": "Godot Blog", + "url": "https://godotengine.org/rss.xml", + }, +] + + +@onready var _feed_list := %RssFeedList as VBoxList +@onready var _refresh_button := %RefreshButton as Button + +var _http_request: HTTPRequest +var _data_loaded := false +var _fetching := false + + +func _ready() -> void: + _http_request = HTTPRequest.new() + add_child(_http_request) + _refresh_button.icon = get_theme_icon("Reload", "EditorIcons") + _refresh_button.pressed.connect(_refetch_data) + + +func init() -> void: + if visible: + _refetch_data() + + +func _refetch_data() -> void: + if _fetching: + return + _fetching = true + _refresh_button.disabled = true + + var all_items: Array = [] + for source in FEED_SOURCES: + var feed_items := await _fetch_feed(source) + all_items.append_array(feed_items) + + # Sort newest first (ISO 8601 and RFC 2822 dates sort lexicographically) + all_items.sort_custom(func(a: RssFeed.FeedItem, b: RssFeed.FeedItem) -> bool: + return a.published > b.published + ) + + _feed_list.refresh(all_items) + _data_loaded = true + _fetching = false + _refresh_button.disabled = false + + +func _fetch_feed(source: Dictionary) -> Array: + var headers := PackedStringArray([Config.AGENT_HEADER]) + _http_request.request(source.url, headers, HTTPClient.METHOD_GET) + var response: Array = await _http_request.request_completed + var response_obj := HttpClient.Response.new(response) + + if response_obj.code != 200: + return [] + + var body := XML.parse_buffer(response_obj.body) + if not body or not body.root: + return [] + + return _parse_feed(body.root, source) + + +func _parse_feed(root: XMLNode, source: Dictionary) -> Array: + var items: Array = [] + # YouTube (Atom) uses , standard RSS uses + for child: XMLNode in root.children: + if child.name == "entry" or child.name == "item": + var item := _parse_entry(child, source.name) + if item and not item.title.is_empty(): + items.append(item) + return items + + +func _parse_entry(entry: XMLNode, source_name: String) -> RssFeed.FeedItem: + var item := RssFeed.FeedItem.new() + item.source_name = source_name + var smart_entry := exml.smart(entry) + + # Title + var title_node := smart_entry.find_smart_child_recursive( + exml.Filters.by_name("title") + ) + if title_node: + item.title = title_node.o.content + + # Link + # YouTube (Atom): + # RSS: url + var link_node := smart_entry.find_smart_child_recursive( + exml.Filters.by_name("link") + ) + if link_node: + if link_node.o.attributes.has("href"): + item.link = link_node.o.attributes["href"] + else: + item.link = link_node.o.content + + # Published date + # YouTube (Atom): , RSS: + var pub_node := smart_entry.find_smart_child_recursive( + exml.Filters.by_name("published") + ) + if not pub_node: + pub_node = smart_entry.find_smart_child_recursive( + exml.Filters.by_name("pubDate") + ) + if pub_node: + item.published = pub_node.o.content + + # Thumbnail (YouTube): + var thumb_node := _find_deep(entry, "media:thumbnail") + if thumb_node: + item.thumbnail_url = thumb_node.attributes.get("url", "") + + # Description + # YouTube: , RSS: + var desc_node := _find_deep(entry, "media:description") + if not desc_node: + desc_node = smart_entry.find_smart_child_recursive( + exml.Filters.by_name("description") + ) + if desc_node: + item.description = desc_node.content + + return item + + +func _find_deep(node: XMLNode, name: String) -> XMLNode: + for child: XMLNode in node.children: + if child.name == name: + return child + var found := _find_deep(child, name) + if found: + return found + return null + + +func _on_visibility_changed() -> void: + if visible and not _data_loaded: + _refetch_data() diff --git a/src/components/rss_feed/rss_feed.tscn b/src/components/rss_feed/rss_feed.tscn new file mode 100644 index 00000000..53a15dd7 --- /dev/null +++ b/src/components/rss_feed/rss_feed.tscn @@ -0,0 +1,27 @@ +[gd_scene load_steps=4 format=3] + +[ext_resource type="Script" path="res://src/components/rss_feed/rss_feed.gd" id="1_script"] +[ext_resource type="PackedScene" path="res://src/components/rss_feed/list/rss_feed_list.tscn" id="2_list"] +[ext_resource type="Script" path="res://src/components/misc/themed_button.gd" id="3_button"] + +[node name="RssFeed" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_script") + +[node name="RssFeedList" parent="." instance=ExtResource("2_list")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="RefreshButton" type="Button" parent="RssFeedList/HBoxContainer" index="2"] +unique_name_in_owner = true +layout_mode = 2 +flat = true +script = ExtResource("3_button") +_theme_icon_name = "Reload" + +[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"] diff --git a/src/components/rss_feed/rss_feed_class.gd b/src/components/rss_feed/rss_feed_class.gd new file mode 100644 index 00000000..a5374c24 --- /dev/null +++ b/src/components/rss_feed/rss_feed_class.gd @@ -0,0 +1,10 @@ +class_name RssFeed + + +class FeedItem extends RefCounted: + var title: String = "" + var link: String = "" + var published: String = "" + var source_name: String = "" + var thumbnail_url: String = "" + var description: String = "" diff --git a/src/main/gui/gui_main.gd b/src/main/gui/gui_main.gd index 4c0fb676..de458c52 100644 --- a/src/main/gui/gui_main.gd +++ b/src/main/gui/gui_main.gd @@ -7,6 +7,7 @@ const theme_source = preload("res://theme/theme.gd") @export var _projects: Control @export var _asset_lib_projects: AssetLibProjects @export var _godots_releases: Control +@export var _rss_feed: RssFeedControl @export var _auto_updates: Node @export var _asset_download: PackedScene @export var _title_tabs: BoxContainer @@ -65,6 +66,7 @@ func _ready(): _title_tabs.add_child(TitleTabButton.new("GodotMonochrome", tr("Editors"), _tab_container, [_local_editors, _remote_editors])) #_title_tabs.add_child(TitleTabButton.new("GodotMonochrome", tr("Remote Editors"), _tab_container, _remote_editors)) #_title_tabs.add_child(TitleTabButton.new(null, tr("Updates"), _tab_container, _updates)) + _title_tabs.add_child(TitleTabButton.new("ExternalLink", tr("Feed"), _tab_container, [_rss_feed])) _gui_base.set( "theme_override_styles/panel", @@ -128,6 +130,7 @@ func _ready(): _projects.init(_projects_service) _local_editors.init(_local_editors_service) _remote_editors.init(%DownloadsContainer) + _rss_feed.init() _projects.manage_tags_requested.connect(_popup_manage_tags) _local_editors.manage_tags_requested.connect(_popup_manage_tags) @@ -164,8 +167,8 @@ func _enter_tree(): DisplayServer.window_set_size(window_size) if screen_rect.size != Vector2i(): var window_position = Vector2i( - screen_rect.position.x + (screen_rect.size.x - window_size.x) / 2, - screen_rect.position.y + (screen_rect.size.y - window_size.y) / 2 + screen_rect.position.x + (screen_rect.size.x - window_size.x) / 2, + screen_rect.position.y + (screen_rect.size.y - window_size.y) / 2 ) DisplayServer.window_set_position(window_position) @@ -341,4 +344,4 @@ class TitleTabButton extends Button: if what == NOTIFICATION_THEME_CHANGED: if _icon_name: self.icon = get_theme_icon(_icon_name, "EditorIcons") - #theme_type_variation = "MainScreenButton" + #theme_type_variation = "MainScreenButton" diff --git a/src/main/gui/gui_main.tscn b/src/main/gui/gui_main.tscn index 07dd14b9..912fb253 100644 --- a/src/main/gui/gui_main.tscn +++ b/src/main/gui/gui_main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=16 format=3 uid="uid://omgg45dpxftx"] +[gd_scene load_steps=17 format=3 uid="uid://omgg45dpxftx"] [ext_resource type="Script" path="res://src/main/gui/gui_main.gd" id="1_yjveq"] [ext_resource type="PackedScene" uid="uid://cb6u1mub27xo" path="res://src/components/asset_download/asset_download.tscn" id="2_gfvot"] @@ -15,8 +15,9 @@ [ext_resource type="PackedScene" uid="uid://buwhvihmtjeff" path="res://src/components/tags/manage_tags.tscn" id="12_6d5cn"] [ext_resource type="PackedScene" uid="uid://btxxnjmhy4bqw" path="res://src/components/command_viewer/command_viewer.tscn" id="13_mk5sf"] [ext_resource type="PackedScene" uid="uid://b3aprmu6od0wa" path="res://src/components/settings/settings_window.tscn" id="14_3j6hr"] +[ext_resource type="PackedScene" path="res://src/components/rss_feed/rss_feed.tscn" id="15_rss"] -[node name="Main" type="Control" node_paths=PackedStringArray("_remote_editors", "_local_editors", "_projects", "_asset_lib_projects", "_godots_releases", "_auto_updates", "_title_tabs", "_updates", "_tab_container")] +[node name="Main" type="Control" node_paths=PackedStringArray("_remote_editors", "_local_editors", "_projects", "_asset_lib_projects", "_godots_releases", "_rss_feed", "_auto_updates", "_title_tabs", "_updates", "_tab_container")] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -29,6 +30,7 @@ _local_editors = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/L _projects = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Local Projects") _asset_lib_projects = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Asset Library Projects") _godots_releases = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Updates") +_rss_feed = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/RSS Feed") _auto_updates = NodePath("AutoUpdates") _asset_download = ExtResource("2_gfvot") _title_tabs = NodePath("GuiBase/MainVBox/TitleBar/ButtonsContainer") @@ -101,6 +103,10 @@ layout_mode = 2 visible = false layout_mode = 2 +[node name="RSS Feed" parent="GuiBase/MainVBox/Content/VBoxContainer/TabContainer" instance=ExtResource("15_rss")] +visible = false +layout_mode = 2 + [node name="DownloadsContainer" parent="GuiBase/MainVBox/Content/VBoxContainer" instance=ExtResource("9_drkdu")] unique_name_in_owner = true layout_mode = 2 From d21cf3c6574aa23fec6b190541c549fb1c5abfe3 Mon Sep 17 00:00:00 2001 From: POWERHACK <74624111+POWERHACK69@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:01:31 +0100 Subject: [PATCH 2/6] Fixed Issues --- addons/gd-plug/plug.gd.uid | 1 + addons/godot_xml/xml.gd.uid | 1 + addons/godot_xml/xml_document.gd.uid | 1 + addons/godot_xml/xml_node.gd.uid | 1 + addons/use-context/context.gd.uid | 1 + addons/use-context/context_keys.gd.uid | 1 + addons/use-context/context_node.gd.uid | 1 + addons/use-context/plugin.gd.uid | 1 + addons/uuid.gd.uid | 1 + plug.gd.uid | 1 + project.godot | 10 +++++++--- src/auto_close.gd.uid | 1 + src/auto_native_file_dialog.gd.uid | 1 + src/cache.gd.uid | 1 + src/cli/cli_command.gd.uid | 1 + src/cli/cli_option.gd.uid | 1 + src/cli/commands/default_routes.gd.uid | 1 + src/cli/commands/editor/editor_routes.gd.uid | 1 + src/cli/commands/editor/list/list.gd.uid | 1 + src/cli/commands/exec/exec.gd.uid | 1 + src/cli/commands/help/help.gd.uid | 1 + .../project/open/open_recent_project.gd.uid | 1 + src/cli/ctx.gd.uid | 1 + src/cli/godots_commands.gd.uid | 1 + src/cli/parser/cli_grammar.gd.uid | 1 + src/cli/parser/cli_parser.gd.uid | 1 + src/cli/root_routes.gd.uid | 1 + src/cli/routes/routes.gd.uid | 1 + .../actions_sidebar/actions_sidebar.gd.uid | 1 + .../asset_download/asset_download.gd.uid | 1 + .../asset_lib_projects/asset_lib.gd.uid | 1 + .../asset_lib_item_details.gd.uid | 1 + .../description_label.gd.uid | 1 + .../asset_lib_projects.gd.uid | 1 + .../asset_list_item/asset_list_item.gd.uid | 1 + .../asset_lib_projects/assets_container.gd.uid | 1 + .../category_option_button.gd.uid | 1 + .../asset_lib_projects/error_container.gd.uid | 1 + .../asset_lib_projects/filter_edit.gd.uid | 1 + .../pagination_container.gd.uid | 1 + .../retry_button_container.gd.uid | 1 + .../asset_lib_projects/scroll_container.gd.uid | 1 + .../site_option_button.gd.uid | 1 + .../sort_option_button.gd.uid | 1 + .../support_menu_button.gd.uid | 1 + .../version_option_button.gd.uid | 1 + .../command_viewer/command_text_view.gd.uid | 1 + .../command_viewer/command_viewer.gd.uid | 1 + .../command_viewer/new_command_dialog.gd.uid | 1 + .../downloads_container.gd.uid | 1 + .../local/editor_import/editor_import.gd.uid | 1 + .../add_extra_arguments_editor_dialog.gd.uid | 1 + .../local/editor_item/editor_item.gd.uid | 1 + .../editor_item/editor_item_actions.gd.uid | 1 + .../editor_item/rename_editor_dialog.gd.uid | 1 + .../local/editor_item/show_owners_dialog.gd.uid | 1 + .../local/editors_list/editors_list.gd.uid | 1 + .../editors/local/local_editors.gd.uid | 1 + .../orphan_editor_explorer.gd.uid | 1 + .../editors/local_remote_editors_switch.gd.uid | 1 + .../local_remote_editors_switch_context.gd.uid | 1 + .../remote_editor_direct_link.gd.uid | 1 + .../remote_editor_direct_link.tscn | 17 +++++++++-------- .../remote_editor_install.gd.uid | 1 + .../editors/remote/remote_editors.gd.uid | 1 + .../remote_editors_tree/data_source.gd.uid | 1 + .../remote_editors_tree.gd.uid | 1 + .../remote_editors_tree/sources/github.gd.uid | 1 + .../godots_releases/godots_releases.gd.uid | 1 + .../list/godots_release_item.gd.uid | 1 + .../list/godots_releases_list.gd.uid | 1 + src/components/misc/array_edit.gd.uid | 1 + .../misc/confirmation_dialog_auto_free.gd.uid | 1 + src/components/misc/custom_minimum_size.gd.uid | 1 + src/components/misc/favorite_button.gd.uid | 1 + src/components/misc/item_tag_container.gd.uid | 1 + src/components/misc/list_item_icon.gd.uid | 1 + src/components/misc/list_item_path_label.gd.uid | 1 + .../misc/list_item_title_label.gd.uid | 1 + src/components/misc/news_button.gd.uid | 1 + src/components/misc/notifications_button.gd.uid | 1 + .../misc/remove_missing_dialog.gd.uid | 1 + src/components/misc/scan_file_dialog.gd.uid | 1 + src/components/misc/tab_actions.gd.uid | 1 + src/components/misc/themed_button.gd.uid | 1 + .../clone_project_dialog.gd.uid | 1 + .../duplicate_project_dialog.gd.uid | 1 + .../import_project_dialog.gd.uid | 1 + .../install_project_dialog.gd.uid | 1 + .../install_project_simple.gd.uid | 1 + .../new_project_dialog.gd.uid | 1 + .../projects/project_item/project_item.gd | 1 - .../projects/project_item/project_item.gd.uid | 1 + .../project_item/project_item_actions.gd.uid | 1 + src/components/projects/projects.gd.uid | 1 + .../projects/projects_list/projects_list.gd.uid | 1 + .../rss_feed/list/rss_feed_item.gd.uid | 1 + .../rss_feed/list/rss_feed_list.gd.uid | 1 + src/components/rss_feed/rss_feed.gd | 4 +++- src/components/rss_feed/rss_feed.gd.uid | 1 + src/components/rss_feed/rss_feed_class.gd.uid | 1 + src/components/settings/settings_window.gd.uid | 1 + src/components/tags/manage_tags.gd.uid | 1 + src/components/tags/tag/tag.gd.uid | 1 + src/components/title_bar/title_bar.gd.uid | 1 + .../v_box_list/item/h_box_list_item.gd.uid | 1 + src/components/v_box_list/v_box_list.gd.uid | 1 + src/config.gd.uid | 1 + src/extensions/buttons.gd.uid | 1 + src/extensions/dict.gd.uid | 1 + src/extensions/dir.gd.uid | 1 + src/extensions/xml.gd.uid | 1 + src/extensions/zip.gd.uid | 1 + src/http_client.gd.uid | 1 + src/main/cli/cli_main.gd.uid | 1 + src/main/gui/auto_updates.gd.uid | 1 + src/main/gui/gui_main.gd.uid | 1 + src/main/main.gd | 8 ++++---- src/main/main.gd.uid | 1 + src/objects/action.gd.uid | 1 + src/objects/config_file_save_on_set.gd.uid | 1 + src/objects/config_file_section.gd.uid | 1 + src/objects/config_file_value.gd.uid | 1 + src/objects/custom_commands_popup_items.gd.uid | 1 + src/objects/node_component/_component.gd.uid | 1 + src/objects/node_component/comp.gd.uid | 1 + src/objects/node_component/comp_init.gd.uid | 1 + src/objects/node_component/comp_refs.gd.uid | 1 + src/objects/node_component/comp_scene.gd.uid | 1 + src/objects/os_process_schema.gd.uid | 1 + src/objects/random_project_names.gd.uid | 1 + src/objects/set.gd.uid | 1 + src/output.gd.uid | 1 + src/services/godots_downloads.gd.uid | 1 + src/services/godots_install.gd.uid | 1 + src/services/godots_recent_releases.gd.uid | 1 + src/services/godots_releases.gd.uid | 1 + src/services/local_editors.gd.uid | 1 + src/services/projects.gd.uid | 1 + src/services/remote_image_src.gd.uid | 1 + src/services/version_hint.gd.uid | 1 + src/utils.gd.uid | 1 + tests/cases/cli/parser/parser_not_ok.gd.uid | 1 + tests/cases/cli/parser/parser_ok.gd.uid | 1 + tests/cases/cli/parser/test_grammar.gd.uid | 1 + .../list_dir_recursive/list_dir_test.gd.uid | 1 + tests/cases/services/local_editor_tests.gd.uid | 1 + theme/fill_icons_registry.gd.uid | 1 + theme/fonts/DroidSansFallback.woff2.import | 3 +++ theme/fonts/DroidSansJapanese.woff2.import | 3 +++ theme/fonts/JetBrainsMono_Regular.woff2.import | 3 +++ theme/fonts/NotoNaskhArabicUI_Bold.woff2.import | 3 +++ .../NotoNaskhArabicUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansBengaliUI_Bold.woff2.import | 3 +++ .../NotoSansBengaliUI_Regular.woff2.import | 3 +++ .../NotoSansDevanagariUI_Bold.woff2.import | 3 +++ .../NotoSansDevanagariUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansGeorgian_Bold.woff2.import | 3 +++ .../fonts/NotoSansGeorgian_Regular.woff2.import | 3 +++ theme/fonts/NotoSansHebrew_Bold.woff2.import | 3 +++ theme/fonts/NotoSansHebrew_Regular.woff2.import | 3 +++ .../fonts/NotoSansMalayalamUI_Bold.woff2.import | 3 +++ .../NotoSansMalayalamUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansOriyaUI_Bold.woff2.import | 3 +++ .../fonts/NotoSansOriyaUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansSinhalaUI_Bold.woff2.import | 3 +++ .../NotoSansSinhalaUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansTamilUI_Bold.woff2.import | 3 +++ .../fonts/NotoSansTamilUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansTeluguUI_Bold.woff2.import | 3 +++ .../fonts/NotoSansTeluguUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSansThaiUI_Bold.woff2.import | 3 +++ theme/fonts/NotoSansThaiUI_Regular.woff2.import | 3 +++ theme/fonts/NotoSans_Bold.woff2.import | 3 +++ theme/fonts/NotoSans_Regular.woff2.import | 3 +++ theme/fonts/OpenSans_SemiBold.woff2.import | 3 +++ theme/theme.gd.uid | 1 + 177 files changed, 251 insertions(+), 17 deletions(-) create mode 100644 addons/gd-plug/plug.gd.uid create mode 100644 addons/godot_xml/xml.gd.uid create mode 100644 addons/godot_xml/xml_document.gd.uid create mode 100644 addons/godot_xml/xml_node.gd.uid create mode 100644 addons/use-context/context.gd.uid create mode 100644 addons/use-context/context_keys.gd.uid create mode 100644 addons/use-context/context_node.gd.uid create mode 100644 addons/use-context/plugin.gd.uid create mode 100644 addons/uuid.gd.uid create mode 100644 plug.gd.uid create mode 100644 src/auto_close.gd.uid create mode 100644 src/auto_native_file_dialog.gd.uid create mode 100644 src/cache.gd.uid create mode 100644 src/cli/cli_command.gd.uid create mode 100644 src/cli/cli_option.gd.uid create mode 100644 src/cli/commands/default_routes.gd.uid create mode 100644 src/cli/commands/editor/editor_routes.gd.uid create mode 100644 src/cli/commands/editor/list/list.gd.uid create mode 100644 src/cli/commands/exec/exec.gd.uid create mode 100644 src/cli/commands/help/help.gd.uid create mode 100644 src/cli/commands/project/open/open_recent_project.gd.uid create mode 100644 src/cli/ctx.gd.uid create mode 100644 src/cli/godots_commands.gd.uid create mode 100644 src/cli/parser/cli_grammar.gd.uid create mode 100644 src/cli/parser/cli_parser.gd.uid create mode 100644 src/cli/root_routes.gd.uid create mode 100644 src/cli/routes/routes.gd.uid create mode 100644 src/components/actions_sidebar/actions_sidebar.gd.uid create mode 100644 src/components/asset_download/asset_download.gd.uid create mode 100644 src/components/asset_lib_projects/asset_lib.gd.uid create mode 100644 src/components/asset_lib_projects/asset_lib_item_details/asset_lib_item_details.gd.uid create mode 100644 src/components/asset_lib_projects/asset_lib_item_details/description_label.gd.uid create mode 100644 src/components/asset_lib_projects/asset_lib_projects.gd.uid create mode 100644 src/components/asset_lib_projects/asset_list_item/asset_list_item.gd.uid create mode 100644 src/components/asset_lib_projects/assets_container.gd.uid create mode 100644 src/components/asset_lib_projects/category_option_button.gd.uid create mode 100644 src/components/asset_lib_projects/error_container.gd.uid create mode 100644 src/components/asset_lib_projects/filter_edit.gd.uid create mode 100644 src/components/asset_lib_projects/pagination_container.gd.uid create mode 100644 src/components/asset_lib_projects/retry_button_container.gd.uid create mode 100644 src/components/asset_lib_projects/scroll_container.gd.uid create mode 100644 src/components/asset_lib_projects/site_option_button.gd.uid create mode 100644 src/components/asset_lib_projects/sort_option_button.gd.uid create mode 100644 src/components/asset_lib_projects/support_menu_button.gd.uid create mode 100644 src/components/asset_lib_projects/version_option_button.gd.uid create mode 100644 src/components/command_viewer/command_text_view.gd.uid create mode 100644 src/components/command_viewer/command_viewer.gd.uid create mode 100644 src/components/command_viewer/new_command_dialog.gd.uid create mode 100644 src/components/downloads_container/downloads_container.gd.uid create mode 100644 src/components/editors/local/editor_import/editor_import.gd.uid create mode 100644 src/components/editors/local/editor_item/add_extra_arguments_editor_dialog.gd.uid create mode 100644 src/components/editors/local/editor_item/editor_item.gd.uid create mode 100644 src/components/editors/local/editor_item/editor_item_actions.gd.uid create mode 100644 src/components/editors/local/editor_item/rename_editor_dialog.gd.uid create mode 100644 src/components/editors/local/editor_item/show_owners_dialog.gd.uid create mode 100644 src/components/editors/local/editors_list/editors_list.gd.uid create mode 100644 src/components/editors/local/local_editors.gd.uid create mode 100644 src/components/editors/local/orphan_editor_explorer/orphan_editor_explorer.gd.uid create mode 100644 src/components/editors/local_remote_editors_switch.gd.uid create mode 100644 src/components/editors/local_remote_editors_switch_context.gd.uid create mode 100644 src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.gd.uid create mode 100644 src/components/editors/remote/remote_editor_install/remote_editor_install.gd.uid create mode 100644 src/components/editors/remote/remote_editors.gd.uid create mode 100644 src/components/editors/remote/remote_editors_tree/data_source.gd.uid create mode 100644 src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd.uid create mode 100644 src/components/editors/remote/remote_editors_tree/sources/github.gd.uid create mode 100644 src/components/godots_releases/godots_releases.gd.uid create mode 100644 src/components/godots_releases/list/godots_release_item.gd.uid create mode 100644 src/components/godots_releases/list/godots_releases_list.gd.uid create mode 100644 src/components/misc/array_edit.gd.uid create mode 100644 src/components/misc/confirmation_dialog_auto_free.gd.uid create mode 100644 src/components/misc/custom_minimum_size.gd.uid create mode 100644 src/components/misc/favorite_button.gd.uid create mode 100644 src/components/misc/item_tag_container.gd.uid create mode 100644 src/components/misc/list_item_icon.gd.uid create mode 100644 src/components/misc/list_item_path_label.gd.uid create mode 100644 src/components/misc/list_item_title_label.gd.uid create mode 100644 src/components/misc/news_button.gd.uid create mode 100644 src/components/misc/notifications_button.gd.uid create mode 100644 src/components/misc/remove_missing_dialog.gd.uid create mode 100644 src/components/misc/scan_file_dialog.gd.uid create mode 100644 src/components/misc/tab_actions.gd.uid create mode 100644 src/components/misc/themed_button.gd.uid create mode 100644 src/components/projects/clone_project_dialog/clone_project_dialog.gd.uid create mode 100644 src/components/projects/duplicate_project_dialog/duplicate_project_dialog.gd.uid create mode 100644 src/components/projects/import_project_dialog/import_project_dialog.gd.uid create mode 100644 src/components/projects/install_project_dialog/install_project_dialog.gd.uid create mode 100644 src/components/projects/install_project_dialog/install_project_simple.gd.uid create mode 100644 src/components/projects/new_project_dialog/new_project_dialog.gd.uid create mode 100644 src/components/projects/project_item/project_item.gd.uid create mode 100644 src/components/projects/project_item/project_item_actions.gd.uid create mode 100644 src/components/projects/projects.gd.uid create mode 100644 src/components/projects/projects_list/projects_list.gd.uid create mode 100644 src/components/rss_feed/list/rss_feed_item.gd.uid create mode 100644 src/components/rss_feed/list/rss_feed_list.gd.uid create mode 100644 src/components/rss_feed/rss_feed.gd.uid create mode 100644 src/components/rss_feed/rss_feed_class.gd.uid create mode 100644 src/components/settings/settings_window.gd.uid create mode 100644 src/components/tags/manage_tags.gd.uid create mode 100644 src/components/tags/tag/tag.gd.uid create mode 100644 src/components/title_bar/title_bar.gd.uid create mode 100644 src/components/v_box_list/item/h_box_list_item.gd.uid create mode 100644 src/components/v_box_list/v_box_list.gd.uid create mode 100644 src/config.gd.uid create mode 100644 src/extensions/buttons.gd.uid create mode 100644 src/extensions/dict.gd.uid create mode 100644 src/extensions/dir.gd.uid create mode 100644 src/extensions/xml.gd.uid create mode 100644 src/extensions/zip.gd.uid create mode 100644 src/http_client.gd.uid create mode 100644 src/main/cli/cli_main.gd.uid create mode 100644 src/main/gui/auto_updates.gd.uid create mode 100644 src/main/gui/gui_main.gd.uid create mode 100644 src/main/main.gd.uid create mode 100644 src/objects/action.gd.uid create mode 100644 src/objects/config_file_save_on_set.gd.uid create mode 100644 src/objects/config_file_section.gd.uid create mode 100644 src/objects/config_file_value.gd.uid create mode 100644 src/objects/custom_commands_popup_items.gd.uid create mode 100644 src/objects/node_component/_component.gd.uid create mode 100644 src/objects/node_component/comp.gd.uid create mode 100644 src/objects/node_component/comp_init.gd.uid create mode 100644 src/objects/node_component/comp_refs.gd.uid create mode 100644 src/objects/node_component/comp_scene.gd.uid create mode 100644 src/objects/os_process_schema.gd.uid create mode 100644 src/objects/random_project_names.gd.uid create mode 100644 src/objects/set.gd.uid create mode 100644 src/output.gd.uid create mode 100644 src/services/godots_downloads.gd.uid create mode 100644 src/services/godots_install.gd.uid create mode 100644 src/services/godots_recent_releases.gd.uid create mode 100644 src/services/godots_releases.gd.uid create mode 100644 src/services/local_editors.gd.uid create mode 100644 src/services/projects.gd.uid create mode 100644 src/services/remote_image_src.gd.uid create mode 100644 src/services/version_hint.gd.uid create mode 100644 src/utils.gd.uid create mode 100644 tests/cases/cli/parser/parser_not_ok.gd.uid create mode 100644 tests/cases/cli/parser/parser_ok.gd.uid create mode 100644 tests/cases/cli/parser/test_grammar.gd.uid create mode 100644 tests/cases/dir_extensions/list_dir_recursive/list_dir_test.gd.uid create mode 100644 tests/cases/services/local_editor_tests.gd.uid create mode 100644 theme/fill_icons_registry.gd.uid create mode 100644 theme/theme.gd.uid diff --git a/addons/gd-plug/plug.gd.uid b/addons/gd-plug/plug.gd.uid new file mode 100644 index 00000000..c4245fa1 --- /dev/null +++ b/addons/gd-plug/plug.gd.uid @@ -0,0 +1 @@ +uid://1f0e7lwytq05 diff --git a/addons/godot_xml/xml.gd.uid b/addons/godot_xml/xml.gd.uid new file mode 100644 index 00000000..1bfcbc51 --- /dev/null +++ b/addons/godot_xml/xml.gd.uid @@ -0,0 +1 @@ +uid://chr0ovfgn2bi8 diff --git a/addons/godot_xml/xml_document.gd.uid b/addons/godot_xml/xml_document.gd.uid new file mode 100644 index 00000000..d0e2fd17 --- /dev/null +++ b/addons/godot_xml/xml_document.gd.uid @@ -0,0 +1 @@ +uid://32d2di2afat7 diff --git a/addons/godot_xml/xml_node.gd.uid b/addons/godot_xml/xml_node.gd.uid new file mode 100644 index 00000000..6fb85980 --- /dev/null +++ b/addons/godot_xml/xml_node.gd.uid @@ -0,0 +1 @@ +uid://bx72q2ut6c4ky diff --git a/addons/use-context/context.gd.uid b/addons/use-context/context.gd.uid new file mode 100644 index 00000000..a458bf56 --- /dev/null +++ b/addons/use-context/context.gd.uid @@ -0,0 +1 @@ +uid://dw6nnxtgk0reu diff --git a/addons/use-context/context_keys.gd.uid b/addons/use-context/context_keys.gd.uid new file mode 100644 index 00000000..477900d4 --- /dev/null +++ b/addons/use-context/context_keys.gd.uid @@ -0,0 +1 @@ +uid://dtssx16d07myw diff --git a/addons/use-context/context_node.gd.uid b/addons/use-context/context_node.gd.uid new file mode 100644 index 00000000..028d85d6 --- /dev/null +++ b/addons/use-context/context_node.gd.uid @@ -0,0 +1 @@ +uid://c81coxv48a5x8 diff --git a/addons/use-context/plugin.gd.uid b/addons/use-context/plugin.gd.uid new file mode 100644 index 00000000..11042e06 --- /dev/null +++ b/addons/use-context/plugin.gd.uid @@ -0,0 +1 @@ +uid://c2nfcywog2uu5 diff --git a/addons/uuid.gd.uid b/addons/uuid.gd.uid new file mode 100644 index 00000000..736eb724 --- /dev/null +++ b/addons/uuid.gd.uid @@ -0,0 +1 @@ +uid://ceukf5aotcrbk diff --git a/plug.gd.uid b/plug.gd.uid new file mode 100644 index 00000000..85537198 --- /dev/null +++ b/plug.gd.uid @@ -0,0 +1 @@ +uid://dvse2dunn1x0r diff --git a/project.godot b/project.godot index 947f24be..62e89f91 100644 --- a/project.godot +++ b/project.godot @@ -8,13 +8,17 @@ config_version=5 +[animation] + +compatibility/default_parent_skeleton_in_mesh_instance_3d=true + [application] config/name="Godots" config/description="Ultimate go-to hub for managing your Godot versions and projects!" config/tags=PackedStringArray("application") run/main_scene="res://src/main/main.tscn" -config/features=PackedStringArray("4.2", "GL Compatibility") +config/features=PackedStringArray("4.6", "GL Compatibility") run/low_processor_mode=true boot_splash/bg_color=Color(0.113725, 0.133333, 0.160784, 1) config/icon="res://icon.svg" @@ -26,7 +30,7 @@ Output="*res://src/output.gd" AutoClose="*res://src/auto_close.gd" Cache="*res://src/cache.gd" HttpClient="*res://src/http_client.gd" -Context="*res://addons/use-context/context_node.gd" +Context="*uid://c81coxv48a5x8" AutoNativeFileDialog="*res://src/auto_native_file_dialog.gd" [display] @@ -36,7 +40,7 @@ window/subwindows/embed_subwindows=false [editor_plugins] -enabled=PackedStringArray("res://addons/expand-region/plugin.cfg", "res://addons/find-everywhere/plugin.cfg", "res://addons/previous-tab/plugin.cfg", "res://addons/script-tabs/plugin.cfg", "res://addons/use-context/plugin.cfg") +enabled=PackedStringArray("res://addons/use-context/plugin.cfg", "res://addons/gdUnit4/plugin.cfg") [filesystem] diff --git a/src/auto_close.gd.uid b/src/auto_close.gd.uid new file mode 100644 index 00000000..0a2add49 --- /dev/null +++ b/src/auto_close.gd.uid @@ -0,0 +1 @@ +uid://cp0m3dlq17g6r diff --git a/src/auto_native_file_dialog.gd.uid b/src/auto_native_file_dialog.gd.uid new file mode 100644 index 00000000..05540458 --- /dev/null +++ b/src/auto_native_file_dialog.gd.uid @@ -0,0 +1 @@ +uid://c08027ctcwjcc diff --git a/src/cache.gd.uid b/src/cache.gd.uid new file mode 100644 index 00000000..bf4664fe --- /dev/null +++ b/src/cache.gd.uid @@ -0,0 +1 @@ +uid://cjfop6t2ghgt8 diff --git a/src/cli/cli_command.gd.uid b/src/cli/cli_command.gd.uid new file mode 100644 index 00000000..9f657ef7 --- /dev/null +++ b/src/cli/cli_command.gd.uid @@ -0,0 +1 @@ +uid://5wk21palm2fr diff --git a/src/cli/cli_option.gd.uid b/src/cli/cli_option.gd.uid new file mode 100644 index 00000000..84995482 --- /dev/null +++ b/src/cli/cli_option.gd.uid @@ -0,0 +1 @@ +uid://2jaf8phogpok diff --git a/src/cli/commands/default_routes.gd.uid b/src/cli/commands/default_routes.gd.uid new file mode 100644 index 00000000..d63f713b --- /dev/null +++ b/src/cli/commands/default_routes.gd.uid @@ -0,0 +1 @@ +uid://dg601c6a0vc4e diff --git a/src/cli/commands/editor/editor_routes.gd.uid b/src/cli/commands/editor/editor_routes.gd.uid new file mode 100644 index 00000000..fb29f75e --- /dev/null +++ b/src/cli/commands/editor/editor_routes.gd.uid @@ -0,0 +1 @@ +uid://bemmsipsrle6y diff --git a/src/cli/commands/editor/list/list.gd.uid b/src/cli/commands/editor/list/list.gd.uid new file mode 100644 index 00000000..9749a453 --- /dev/null +++ b/src/cli/commands/editor/list/list.gd.uid @@ -0,0 +1 @@ +uid://c88mn61jde3lb diff --git a/src/cli/commands/exec/exec.gd.uid b/src/cli/commands/exec/exec.gd.uid new file mode 100644 index 00000000..fea2acf8 --- /dev/null +++ b/src/cli/commands/exec/exec.gd.uid @@ -0,0 +1 @@ +uid://dyh0bnl5nppt diff --git a/src/cli/commands/help/help.gd.uid b/src/cli/commands/help/help.gd.uid new file mode 100644 index 00000000..39f5ea3c --- /dev/null +++ b/src/cli/commands/help/help.gd.uid @@ -0,0 +1 @@ +uid://daugvpgojekxm diff --git a/src/cli/commands/project/open/open_recent_project.gd.uid b/src/cli/commands/project/open/open_recent_project.gd.uid new file mode 100644 index 00000000..eb808a3d --- /dev/null +++ b/src/cli/commands/project/open/open_recent_project.gd.uid @@ -0,0 +1 @@ +uid://dgiaxwn0jyvu diff --git a/src/cli/ctx.gd.uid b/src/cli/ctx.gd.uid new file mode 100644 index 00000000..1d5f440b --- /dev/null +++ b/src/cli/ctx.gd.uid @@ -0,0 +1 @@ +uid://nevdhma1n7qw diff --git a/src/cli/godots_commands.gd.uid b/src/cli/godots_commands.gd.uid new file mode 100644 index 00000000..4ee0654c --- /dev/null +++ b/src/cli/godots_commands.gd.uid @@ -0,0 +1 @@ +uid://ola1p0phop6e diff --git a/src/cli/parser/cli_grammar.gd.uid b/src/cli/parser/cli_grammar.gd.uid new file mode 100644 index 00000000..0d19fdb3 --- /dev/null +++ b/src/cli/parser/cli_grammar.gd.uid @@ -0,0 +1 @@ +uid://dwouisp5h8730 diff --git a/src/cli/parser/cli_parser.gd.uid b/src/cli/parser/cli_parser.gd.uid new file mode 100644 index 00000000..fe77c136 --- /dev/null +++ b/src/cli/parser/cli_parser.gd.uid @@ -0,0 +1 @@ +uid://djgkgl4ioox2a diff --git a/src/cli/root_routes.gd.uid b/src/cli/root_routes.gd.uid new file mode 100644 index 00000000..41e5f730 --- /dev/null +++ b/src/cli/root_routes.gd.uid @@ -0,0 +1 @@ +uid://m7d46xpwkylu diff --git a/src/cli/routes/routes.gd.uid b/src/cli/routes/routes.gd.uid new file mode 100644 index 00000000..9c53eec7 --- /dev/null +++ b/src/cli/routes/routes.gd.uid @@ -0,0 +1 @@ +uid://cojccio0bkhp7 diff --git a/src/components/actions_sidebar/actions_sidebar.gd.uid b/src/components/actions_sidebar/actions_sidebar.gd.uid new file mode 100644 index 00000000..15a44c62 --- /dev/null +++ b/src/components/actions_sidebar/actions_sidebar.gd.uid @@ -0,0 +1 @@ +uid://cs25fwi1eiq52 diff --git a/src/components/asset_download/asset_download.gd.uid b/src/components/asset_download/asset_download.gd.uid new file mode 100644 index 00000000..b56e09f1 --- /dev/null +++ b/src/components/asset_download/asset_download.gd.uid @@ -0,0 +1 @@ +uid://b5yjhxlso16xw diff --git a/src/components/asset_lib_projects/asset_lib.gd.uid b/src/components/asset_lib_projects/asset_lib.gd.uid new file mode 100644 index 00000000..9f64cafb --- /dev/null +++ b/src/components/asset_lib_projects/asset_lib.gd.uid @@ -0,0 +1 @@ +uid://c27x330wo1m8d diff --git a/src/components/asset_lib_projects/asset_lib_item_details/asset_lib_item_details.gd.uid b/src/components/asset_lib_projects/asset_lib_item_details/asset_lib_item_details.gd.uid new file mode 100644 index 00000000..7c73f134 --- /dev/null +++ b/src/components/asset_lib_projects/asset_lib_item_details/asset_lib_item_details.gd.uid @@ -0,0 +1 @@ +uid://do5e8lms3ul1i diff --git a/src/components/asset_lib_projects/asset_lib_item_details/description_label.gd.uid b/src/components/asset_lib_projects/asset_lib_item_details/description_label.gd.uid new file mode 100644 index 00000000..1f435b93 --- /dev/null +++ b/src/components/asset_lib_projects/asset_lib_item_details/description_label.gd.uid @@ -0,0 +1 @@ +uid://ch24vo4k3q1b1 diff --git a/src/components/asset_lib_projects/asset_lib_projects.gd.uid b/src/components/asset_lib_projects/asset_lib_projects.gd.uid new file mode 100644 index 00000000..12e93c99 --- /dev/null +++ b/src/components/asset_lib_projects/asset_lib_projects.gd.uid @@ -0,0 +1 @@ +uid://cjo0t5cil6d1k diff --git a/src/components/asset_lib_projects/asset_list_item/asset_list_item.gd.uid b/src/components/asset_lib_projects/asset_list_item/asset_list_item.gd.uid new file mode 100644 index 00000000..e784a2de --- /dev/null +++ b/src/components/asset_lib_projects/asset_list_item/asset_list_item.gd.uid @@ -0,0 +1 @@ +uid://cytx43jj7drvi diff --git a/src/components/asset_lib_projects/assets_container.gd.uid b/src/components/asset_lib_projects/assets_container.gd.uid new file mode 100644 index 00000000..033a2ee6 --- /dev/null +++ b/src/components/asset_lib_projects/assets_container.gd.uid @@ -0,0 +1 @@ +uid://c1xx45807btb diff --git a/src/components/asset_lib_projects/category_option_button.gd.uid b/src/components/asset_lib_projects/category_option_button.gd.uid new file mode 100644 index 00000000..9d5e0810 --- /dev/null +++ b/src/components/asset_lib_projects/category_option_button.gd.uid @@ -0,0 +1 @@ +uid://dnhwkceu8bgbe diff --git a/src/components/asset_lib_projects/error_container.gd.uid b/src/components/asset_lib_projects/error_container.gd.uid new file mode 100644 index 00000000..b7ea4a3b --- /dev/null +++ b/src/components/asset_lib_projects/error_container.gd.uid @@ -0,0 +1 @@ +uid://c3quyjq2xmrt7 diff --git a/src/components/asset_lib_projects/filter_edit.gd.uid b/src/components/asset_lib_projects/filter_edit.gd.uid new file mode 100644 index 00000000..73326ac9 --- /dev/null +++ b/src/components/asset_lib_projects/filter_edit.gd.uid @@ -0,0 +1 @@ +uid://cou3n4cx0cik0 diff --git a/src/components/asset_lib_projects/pagination_container.gd.uid b/src/components/asset_lib_projects/pagination_container.gd.uid new file mode 100644 index 00000000..0b8d5e8e --- /dev/null +++ b/src/components/asset_lib_projects/pagination_container.gd.uid @@ -0,0 +1 @@ +uid://bmxyvjlm67a18 diff --git a/src/components/asset_lib_projects/retry_button_container.gd.uid b/src/components/asset_lib_projects/retry_button_container.gd.uid new file mode 100644 index 00000000..f49bd5fa --- /dev/null +++ b/src/components/asset_lib_projects/retry_button_container.gd.uid @@ -0,0 +1 @@ +uid://dyr53brrpshqw diff --git a/src/components/asset_lib_projects/scroll_container.gd.uid b/src/components/asset_lib_projects/scroll_container.gd.uid new file mode 100644 index 00000000..10a00aab --- /dev/null +++ b/src/components/asset_lib_projects/scroll_container.gd.uid @@ -0,0 +1 @@ +uid://b55xm57vfj5o2 diff --git a/src/components/asset_lib_projects/site_option_button.gd.uid b/src/components/asset_lib_projects/site_option_button.gd.uid new file mode 100644 index 00000000..d0914a4a --- /dev/null +++ b/src/components/asset_lib_projects/site_option_button.gd.uid @@ -0,0 +1 @@ +uid://dtfqy17ndupkq diff --git a/src/components/asset_lib_projects/sort_option_button.gd.uid b/src/components/asset_lib_projects/sort_option_button.gd.uid new file mode 100644 index 00000000..e43ed206 --- /dev/null +++ b/src/components/asset_lib_projects/sort_option_button.gd.uid @@ -0,0 +1 @@ +uid://d3wkdsvl54t56 diff --git a/src/components/asset_lib_projects/support_menu_button.gd.uid b/src/components/asset_lib_projects/support_menu_button.gd.uid new file mode 100644 index 00000000..e83529fb --- /dev/null +++ b/src/components/asset_lib_projects/support_menu_button.gd.uid @@ -0,0 +1 @@ +uid://6c2vex7g5cl7 diff --git a/src/components/asset_lib_projects/version_option_button.gd.uid b/src/components/asset_lib_projects/version_option_button.gd.uid new file mode 100644 index 00000000..17886bf9 --- /dev/null +++ b/src/components/asset_lib_projects/version_option_button.gd.uid @@ -0,0 +1 @@ +uid://6scpn7l4t0gg diff --git a/src/components/command_viewer/command_text_view.gd.uid b/src/components/command_viewer/command_text_view.gd.uid new file mode 100644 index 00000000..d7ec1cb0 --- /dev/null +++ b/src/components/command_viewer/command_text_view.gd.uid @@ -0,0 +1 @@ +uid://j62emu6knxl8 diff --git a/src/components/command_viewer/command_viewer.gd.uid b/src/components/command_viewer/command_viewer.gd.uid new file mode 100644 index 00000000..4ba6139d --- /dev/null +++ b/src/components/command_viewer/command_viewer.gd.uid @@ -0,0 +1 @@ +uid://ct0lj5nn7d0kb diff --git a/src/components/command_viewer/new_command_dialog.gd.uid b/src/components/command_viewer/new_command_dialog.gd.uid new file mode 100644 index 00000000..03d9b557 --- /dev/null +++ b/src/components/command_viewer/new_command_dialog.gd.uid @@ -0,0 +1 @@ +uid://b8t1fub206xj3 diff --git a/src/components/downloads_container/downloads_container.gd.uid b/src/components/downloads_container/downloads_container.gd.uid new file mode 100644 index 00000000..e31548ac --- /dev/null +++ b/src/components/downloads_container/downloads_container.gd.uid @@ -0,0 +1 @@ +uid://diqhx10q6a1ui diff --git a/src/components/editors/local/editor_import/editor_import.gd.uid b/src/components/editors/local/editor_import/editor_import.gd.uid new file mode 100644 index 00000000..7d379da1 --- /dev/null +++ b/src/components/editors/local/editor_import/editor_import.gd.uid @@ -0,0 +1 @@ +uid://7ew1luyl5fq diff --git a/src/components/editors/local/editor_item/add_extra_arguments_editor_dialog.gd.uid b/src/components/editors/local/editor_item/add_extra_arguments_editor_dialog.gd.uid new file mode 100644 index 00000000..fcf53fda --- /dev/null +++ b/src/components/editors/local/editor_item/add_extra_arguments_editor_dialog.gd.uid @@ -0,0 +1 @@ +uid://d1dd1xgjr8lq6 diff --git a/src/components/editors/local/editor_item/editor_item.gd.uid b/src/components/editors/local/editor_item/editor_item.gd.uid new file mode 100644 index 00000000..588fdaf0 --- /dev/null +++ b/src/components/editors/local/editor_item/editor_item.gd.uid @@ -0,0 +1 @@ +uid://cxpg6cwbxwga8 diff --git a/src/components/editors/local/editor_item/editor_item_actions.gd.uid b/src/components/editors/local/editor_item/editor_item_actions.gd.uid new file mode 100644 index 00000000..00afbf78 --- /dev/null +++ b/src/components/editors/local/editor_item/editor_item_actions.gd.uid @@ -0,0 +1 @@ +uid://cmxwodiq7hqd1 diff --git a/src/components/editors/local/editor_item/rename_editor_dialog.gd.uid b/src/components/editors/local/editor_item/rename_editor_dialog.gd.uid new file mode 100644 index 00000000..263951cf --- /dev/null +++ b/src/components/editors/local/editor_item/rename_editor_dialog.gd.uid @@ -0,0 +1 @@ +uid://ewprwq3wupbc diff --git a/src/components/editors/local/editor_item/show_owners_dialog.gd.uid b/src/components/editors/local/editor_item/show_owners_dialog.gd.uid new file mode 100644 index 00000000..b555fe1b --- /dev/null +++ b/src/components/editors/local/editor_item/show_owners_dialog.gd.uid @@ -0,0 +1 @@ +uid://b0ycegltr46gd diff --git a/src/components/editors/local/editors_list/editors_list.gd.uid b/src/components/editors/local/editors_list/editors_list.gd.uid new file mode 100644 index 00000000..0fef0d28 --- /dev/null +++ b/src/components/editors/local/editors_list/editors_list.gd.uid @@ -0,0 +1 @@ +uid://cytv2g64gkcoc diff --git a/src/components/editors/local/local_editors.gd.uid b/src/components/editors/local/local_editors.gd.uid new file mode 100644 index 00000000..b1f9a4ee --- /dev/null +++ b/src/components/editors/local/local_editors.gd.uid @@ -0,0 +1 @@ +uid://chfvicifd18o5 diff --git a/src/components/editors/local/orphan_editor_explorer/orphan_editor_explorer.gd.uid b/src/components/editors/local/orphan_editor_explorer/orphan_editor_explorer.gd.uid new file mode 100644 index 00000000..8ca84d53 --- /dev/null +++ b/src/components/editors/local/orphan_editor_explorer/orphan_editor_explorer.gd.uid @@ -0,0 +1 @@ +uid://c1qij1xf2smku diff --git a/src/components/editors/local_remote_editors_switch.gd.uid b/src/components/editors/local_remote_editors_switch.gd.uid new file mode 100644 index 00000000..c6f5d2d4 --- /dev/null +++ b/src/components/editors/local_remote_editors_switch.gd.uid @@ -0,0 +1 @@ +uid://h2gnkydq08rr diff --git a/src/components/editors/local_remote_editors_switch_context.gd.uid b/src/components/editors/local_remote_editors_switch_context.gd.uid new file mode 100644 index 00000000..2efb7f3f --- /dev/null +++ b/src/components/editors/local_remote_editors_switch_context.gd.uid @@ -0,0 +1 @@ +uid://bfnl6tm5icyt diff --git a/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.gd.uid b/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.gd.uid new file mode 100644 index 00000000..87aa06dc --- /dev/null +++ b/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.gd.uid @@ -0,0 +1 @@ +uid://da2wilfjmk4s2 diff --git a/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.tscn b/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.tscn index 5255bd40..99a92459 100644 --- a/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.tscn +++ b/src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.tscn @@ -1,34 +1,35 @@ -[gd_scene load_steps=2 format=3 uid="uid://dhvesrdvhm6lv"] +[gd_scene format=3 uid="uid://dhvesrdvhm6lv"] -[ext_resource type="Script" path="res://src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.gd" id="1_nwbsu"] +[ext_resource type="Script" uid="uid://da2wilfjmk4s2" path="res://src/components/editors/remote/remote_editor_direct_link/remote_editor_direct_link.gd" id="1_nwbsu"] -[node name="RemoteEditorDirectLink" type="ConfirmationDialog"] +[node name="RemoteEditorDirectLink" type="ConfirmationDialog" unique_id=602156080] +oversampling_override = 1.0 title = "Enter Link" position = Vector2i(0, 36) size = Vector2i(557, 100) visible = true script = ExtResource("1_nwbsu") -[node name="VBoxContainer" type="VBoxContainer" parent="."] +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=150849877] offset_left = 8.0 offset_top = 8.0 offset_right = 549.0 offset_bottom = 51.0 -[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=1944310026] layout_mode = 2 -[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"] +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer" unique_id=1570362618] layout_mode = 2 text = "Link:" -[node name="UrlEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer"] +[node name="UrlEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer" unique_id=1859611064] unique_name_in_owner = true custom_minimum_size = Vector2(500, 0) layout_mode = 2 size_flags_horizontal = 3 placeholder_text = "https://" -[node name="Control" type="Control" parent="VBoxContainer"] +[node name="Control" type="Control" parent="VBoxContainer" unique_id=1066847016] layout_mode = 2 size_flags_vertical = 3 diff --git a/src/components/editors/remote/remote_editor_install/remote_editor_install.gd.uid b/src/components/editors/remote/remote_editor_install/remote_editor_install.gd.uid new file mode 100644 index 00000000..4e879149 --- /dev/null +++ b/src/components/editors/remote/remote_editor_install/remote_editor_install.gd.uid @@ -0,0 +1 @@ +uid://bup1yxhp6od2l diff --git a/src/components/editors/remote/remote_editors.gd.uid b/src/components/editors/remote/remote_editors.gd.uid new file mode 100644 index 00000000..9a95a447 --- /dev/null +++ b/src/components/editors/remote/remote_editors.gd.uid @@ -0,0 +1 @@ +uid://brkmoi15pd1xg diff --git a/src/components/editors/remote/remote_editors_tree/data_source.gd.uid b/src/components/editors/remote/remote_editors_tree/data_source.gd.uid new file mode 100644 index 00000000..6726c06e --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/data_source.gd.uid @@ -0,0 +1 @@ +uid://ccjvo5lixbndj diff --git a/src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd.uid b/src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd.uid new file mode 100644 index 00000000..7416e50a --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/remote_editors_tree.gd.uid @@ -0,0 +1 @@ +uid://cxcl0gg85mqir diff --git a/src/components/editors/remote/remote_editors_tree/sources/github.gd.uid b/src/components/editors/remote/remote_editors_tree/sources/github.gd.uid new file mode 100644 index 00000000..6c387ae3 --- /dev/null +++ b/src/components/editors/remote/remote_editors_tree/sources/github.gd.uid @@ -0,0 +1 @@ +uid://bo5r54efjh5p1 diff --git a/src/components/godots_releases/godots_releases.gd.uid b/src/components/godots_releases/godots_releases.gd.uid new file mode 100644 index 00000000..84fd6e17 --- /dev/null +++ b/src/components/godots_releases/godots_releases.gd.uid @@ -0,0 +1 @@ +uid://btuvm2phutrc5 diff --git a/src/components/godots_releases/list/godots_release_item.gd.uid b/src/components/godots_releases/list/godots_release_item.gd.uid new file mode 100644 index 00000000..7b39d5c4 --- /dev/null +++ b/src/components/godots_releases/list/godots_release_item.gd.uid @@ -0,0 +1 @@ +uid://df27ei1c4u41u diff --git a/src/components/godots_releases/list/godots_releases_list.gd.uid b/src/components/godots_releases/list/godots_releases_list.gd.uid new file mode 100644 index 00000000..63b0ce73 --- /dev/null +++ b/src/components/godots_releases/list/godots_releases_list.gd.uid @@ -0,0 +1 @@ +uid://cdv66wjnnweyk diff --git a/src/components/misc/array_edit.gd.uid b/src/components/misc/array_edit.gd.uid new file mode 100644 index 00000000..43c0c25a --- /dev/null +++ b/src/components/misc/array_edit.gd.uid @@ -0,0 +1 @@ +uid://fv3k4a3a8nkl diff --git a/src/components/misc/confirmation_dialog_auto_free.gd.uid b/src/components/misc/confirmation_dialog_auto_free.gd.uid new file mode 100644 index 00000000..11444932 --- /dev/null +++ b/src/components/misc/confirmation_dialog_auto_free.gd.uid @@ -0,0 +1 @@ +uid://cibnhn7igves4 diff --git a/src/components/misc/custom_minimum_size.gd.uid b/src/components/misc/custom_minimum_size.gd.uid new file mode 100644 index 00000000..385c02b4 --- /dev/null +++ b/src/components/misc/custom_minimum_size.gd.uid @@ -0,0 +1 @@ +uid://b25m1okwk66rv diff --git a/src/components/misc/favorite_button.gd.uid b/src/components/misc/favorite_button.gd.uid new file mode 100644 index 00000000..f44523bb --- /dev/null +++ b/src/components/misc/favorite_button.gd.uid @@ -0,0 +1 @@ +uid://dicuyg2uq3xfg diff --git a/src/components/misc/item_tag_container.gd.uid b/src/components/misc/item_tag_container.gd.uid new file mode 100644 index 00000000..e6028125 --- /dev/null +++ b/src/components/misc/item_tag_container.gd.uid @@ -0,0 +1 @@ +uid://crv1kad7iho35 diff --git a/src/components/misc/list_item_icon.gd.uid b/src/components/misc/list_item_icon.gd.uid new file mode 100644 index 00000000..cc8ed50f --- /dev/null +++ b/src/components/misc/list_item_icon.gd.uid @@ -0,0 +1 @@ +uid://dqsph2gdq1pcy diff --git a/src/components/misc/list_item_path_label.gd.uid b/src/components/misc/list_item_path_label.gd.uid new file mode 100644 index 00000000..48393e7d --- /dev/null +++ b/src/components/misc/list_item_path_label.gd.uid @@ -0,0 +1 @@ +uid://b2f07teiqjwks diff --git a/src/components/misc/list_item_title_label.gd.uid b/src/components/misc/list_item_title_label.gd.uid new file mode 100644 index 00000000..9dd62f00 --- /dev/null +++ b/src/components/misc/list_item_title_label.gd.uid @@ -0,0 +1 @@ +uid://b3ufqis2fwjja diff --git a/src/components/misc/news_button.gd.uid b/src/components/misc/news_button.gd.uid new file mode 100644 index 00000000..0ef64a32 --- /dev/null +++ b/src/components/misc/news_button.gd.uid @@ -0,0 +1 @@ +uid://8elf35pbu73b diff --git a/src/components/misc/notifications_button.gd.uid b/src/components/misc/notifications_button.gd.uid new file mode 100644 index 00000000..2fdf803b --- /dev/null +++ b/src/components/misc/notifications_button.gd.uid @@ -0,0 +1 @@ +uid://cunmpmglcvdsv diff --git a/src/components/misc/remove_missing_dialog.gd.uid b/src/components/misc/remove_missing_dialog.gd.uid new file mode 100644 index 00000000..f4d9013f --- /dev/null +++ b/src/components/misc/remove_missing_dialog.gd.uid @@ -0,0 +1 @@ +uid://bidesw0jinqch diff --git a/src/components/misc/scan_file_dialog.gd.uid b/src/components/misc/scan_file_dialog.gd.uid new file mode 100644 index 00000000..2e7b4a7e --- /dev/null +++ b/src/components/misc/scan_file_dialog.gd.uid @@ -0,0 +1 @@ +uid://dgiw7p70j4ng1 diff --git a/src/components/misc/tab_actions.gd.uid b/src/components/misc/tab_actions.gd.uid new file mode 100644 index 00000000..d30ae759 --- /dev/null +++ b/src/components/misc/tab_actions.gd.uid @@ -0,0 +1 @@ +uid://doc32tfge4kxi diff --git a/src/components/misc/themed_button.gd.uid b/src/components/misc/themed_button.gd.uid new file mode 100644 index 00000000..c4467de7 --- /dev/null +++ b/src/components/misc/themed_button.gd.uid @@ -0,0 +1 @@ +uid://c3y7le17wngkf diff --git a/src/components/projects/clone_project_dialog/clone_project_dialog.gd.uid b/src/components/projects/clone_project_dialog/clone_project_dialog.gd.uid new file mode 100644 index 00000000..766a841c --- /dev/null +++ b/src/components/projects/clone_project_dialog/clone_project_dialog.gd.uid @@ -0,0 +1 @@ +uid://c0rcoq0loj7cn diff --git a/src/components/projects/duplicate_project_dialog/duplicate_project_dialog.gd.uid b/src/components/projects/duplicate_project_dialog/duplicate_project_dialog.gd.uid new file mode 100644 index 00000000..2b7eea09 --- /dev/null +++ b/src/components/projects/duplicate_project_dialog/duplicate_project_dialog.gd.uid @@ -0,0 +1 @@ +uid://x5sf8deuwf4o diff --git a/src/components/projects/import_project_dialog/import_project_dialog.gd.uid b/src/components/projects/import_project_dialog/import_project_dialog.gd.uid new file mode 100644 index 00000000..6584f17a --- /dev/null +++ b/src/components/projects/import_project_dialog/import_project_dialog.gd.uid @@ -0,0 +1 @@ +uid://bggavcdwbk1p7 diff --git a/src/components/projects/install_project_dialog/install_project_dialog.gd.uid b/src/components/projects/install_project_dialog/install_project_dialog.gd.uid new file mode 100644 index 00000000..4c57a12e --- /dev/null +++ b/src/components/projects/install_project_dialog/install_project_dialog.gd.uid @@ -0,0 +1 @@ +uid://f4nffg2qcxw0 diff --git a/src/components/projects/install_project_dialog/install_project_simple.gd.uid b/src/components/projects/install_project_dialog/install_project_simple.gd.uid new file mode 100644 index 00000000..a9a56b00 --- /dev/null +++ b/src/components/projects/install_project_dialog/install_project_simple.gd.uid @@ -0,0 +1 @@ +uid://b357prgxhdex0 diff --git a/src/components/projects/new_project_dialog/new_project_dialog.gd.uid b/src/components/projects/new_project_dialog/new_project_dialog.gd.uid new file mode 100644 index 00000000..9f949475 --- /dev/null +++ b/src/components/projects/new_project_dialog/new_project_dialog.gd.uid @@ -0,0 +1 @@ +uid://dqpi63ys4sl5x diff --git a/src/components/projects/project_item/project_item.gd b/src/components/projects/project_item/project_item.gd index 3bcb0fa9..1e4459dc 100644 --- a/src/components/projects/project_item/project_item.gd +++ b/src/components/projects/project_item/project_item.gd @@ -440,4 +440,3 @@ class RunButton extends Button: ) if item.has_invalid_editor: tooltip_text = tr("Bind editor first.") - diff --git a/src/components/projects/project_item/project_item.gd.uid b/src/components/projects/project_item/project_item.gd.uid new file mode 100644 index 00000000..350e980f --- /dev/null +++ b/src/components/projects/project_item/project_item.gd.uid @@ -0,0 +1 @@ +uid://cbigewfqfrt1f diff --git a/src/components/projects/project_item/project_item_actions.gd.uid b/src/components/projects/project_item/project_item_actions.gd.uid new file mode 100644 index 00000000..dff97df9 --- /dev/null +++ b/src/components/projects/project_item/project_item_actions.gd.uid @@ -0,0 +1 @@ +uid://do7j82ew8psq diff --git a/src/components/projects/projects.gd.uid b/src/components/projects/projects.gd.uid new file mode 100644 index 00000000..0224e95d --- /dev/null +++ b/src/components/projects/projects.gd.uid @@ -0,0 +1 @@ +uid://dycjmdtkicrrs diff --git a/src/components/projects/projects_list/projects_list.gd.uid b/src/components/projects/projects_list/projects_list.gd.uid new file mode 100644 index 00000000..0bbbd6b5 --- /dev/null +++ b/src/components/projects/projects_list/projects_list.gd.uid @@ -0,0 +1 @@ +uid://b0dyq5oyub80j diff --git a/src/components/rss_feed/list/rss_feed_item.gd.uid b/src/components/rss_feed/list/rss_feed_item.gd.uid new file mode 100644 index 00000000..1aa86fe9 --- /dev/null +++ b/src/components/rss_feed/list/rss_feed_item.gd.uid @@ -0,0 +1 @@ +uid://kcj0tywlo0pk diff --git a/src/components/rss_feed/list/rss_feed_list.gd.uid b/src/components/rss_feed/list/rss_feed_list.gd.uid new file mode 100644 index 00000000..c7a0add9 --- /dev/null +++ b/src/components/rss_feed/list/rss_feed_list.gd.uid @@ -0,0 +1 @@ +uid://dfla7da0fs7j0 diff --git a/src/components/rss_feed/rss_feed.gd b/src/components/rss_feed/rss_feed.gd index 7fbcaf5b..38191886 100644 --- a/src/components/rss_feed/rss_feed.gd +++ b/src/components/rss_feed/rss_feed.gd @@ -138,9 +138,11 @@ func _parse_entry(entry: XMLNode, source_name: String) -> RssFeed.FeedItem: # YouTube: , RSS: var desc_node := _find_deep(entry, "media:description") if not desc_node: - desc_node = smart_entry.find_smart_child_recursive( + var smart_desc := smart_entry.find_smart_child_recursive( exml.Filters.by_name("description") ) + if smart_desc: + desc_node = smart_desc.o if desc_node: item.description = desc_node.content diff --git a/src/components/rss_feed/rss_feed.gd.uid b/src/components/rss_feed/rss_feed.gd.uid new file mode 100644 index 00000000..bb0c61e5 --- /dev/null +++ b/src/components/rss_feed/rss_feed.gd.uid @@ -0,0 +1 @@ +uid://cvtuorgvycuy diff --git a/src/components/rss_feed/rss_feed_class.gd.uid b/src/components/rss_feed/rss_feed_class.gd.uid new file mode 100644 index 00000000..489ca459 --- /dev/null +++ b/src/components/rss_feed/rss_feed_class.gd.uid @@ -0,0 +1 @@ +uid://dmhj1tbx1ygnd diff --git a/src/components/settings/settings_window.gd.uid b/src/components/settings/settings_window.gd.uid new file mode 100644 index 00000000..2e747eb3 --- /dev/null +++ b/src/components/settings/settings_window.gd.uid @@ -0,0 +1 @@ +uid://dev80hhurq376 diff --git a/src/components/tags/manage_tags.gd.uid b/src/components/tags/manage_tags.gd.uid new file mode 100644 index 00000000..541616ed --- /dev/null +++ b/src/components/tags/manage_tags.gd.uid @@ -0,0 +1 @@ +uid://dlaq7k4u0ml6r diff --git a/src/components/tags/tag/tag.gd.uid b/src/components/tags/tag/tag.gd.uid new file mode 100644 index 00000000..2d44ba1f --- /dev/null +++ b/src/components/tags/tag/tag.gd.uid @@ -0,0 +1 @@ +uid://fbd5idw0stgp diff --git a/src/components/title_bar/title_bar.gd.uid b/src/components/title_bar/title_bar.gd.uid new file mode 100644 index 00000000..35a3075f --- /dev/null +++ b/src/components/title_bar/title_bar.gd.uid @@ -0,0 +1 @@ +uid://tair7kfgeji5 diff --git a/src/components/v_box_list/item/h_box_list_item.gd.uid b/src/components/v_box_list/item/h_box_list_item.gd.uid new file mode 100644 index 00000000..50521e29 --- /dev/null +++ b/src/components/v_box_list/item/h_box_list_item.gd.uid @@ -0,0 +1 @@ +uid://dknlefq2vq2km diff --git a/src/components/v_box_list/v_box_list.gd.uid b/src/components/v_box_list/v_box_list.gd.uid new file mode 100644 index 00000000..efa88880 --- /dev/null +++ b/src/components/v_box_list/v_box_list.gd.uid @@ -0,0 +1 @@ +uid://ds1mwetp2mkb5 diff --git a/src/config.gd.uid b/src/config.gd.uid new file mode 100644 index 00000000..7f24a2df --- /dev/null +++ b/src/config.gd.uid @@ -0,0 +1 @@ +uid://68phjoulylio diff --git a/src/extensions/buttons.gd.uid b/src/extensions/buttons.gd.uid new file mode 100644 index 00000000..7c91a194 --- /dev/null +++ b/src/extensions/buttons.gd.uid @@ -0,0 +1 @@ +uid://cam1wuv7iyru6 diff --git a/src/extensions/dict.gd.uid b/src/extensions/dict.gd.uid new file mode 100644 index 00000000..195b629e --- /dev/null +++ b/src/extensions/dict.gd.uid @@ -0,0 +1 @@ +uid://bfeir7d2kh8a1 diff --git a/src/extensions/dir.gd.uid b/src/extensions/dir.gd.uid new file mode 100644 index 00000000..023c0636 --- /dev/null +++ b/src/extensions/dir.gd.uid @@ -0,0 +1 @@ +uid://q41v2c53aodc diff --git a/src/extensions/xml.gd.uid b/src/extensions/xml.gd.uid new file mode 100644 index 00000000..1cb21ac6 --- /dev/null +++ b/src/extensions/xml.gd.uid @@ -0,0 +1 @@ +uid://bmyrcwx1q2tns diff --git a/src/extensions/zip.gd.uid b/src/extensions/zip.gd.uid new file mode 100644 index 00000000..0f6315ea --- /dev/null +++ b/src/extensions/zip.gd.uid @@ -0,0 +1 @@ +uid://bohvbf1exjq7b diff --git a/src/http_client.gd.uid b/src/http_client.gd.uid new file mode 100644 index 00000000..7f56f445 --- /dev/null +++ b/src/http_client.gd.uid @@ -0,0 +1 @@ +uid://r4ex57bt4b2w diff --git a/src/main/cli/cli_main.gd.uid b/src/main/cli/cli_main.gd.uid new file mode 100644 index 00000000..9964da95 --- /dev/null +++ b/src/main/cli/cli_main.gd.uid @@ -0,0 +1 @@ +uid://bgwda8wljqimt diff --git a/src/main/gui/auto_updates.gd.uid b/src/main/gui/auto_updates.gd.uid new file mode 100644 index 00000000..054d9454 --- /dev/null +++ b/src/main/gui/auto_updates.gd.uid @@ -0,0 +1 @@ +uid://w8415ey8wiqq diff --git a/src/main/gui/gui_main.gd.uid b/src/main/gui/gui_main.gd.uid new file mode 100644 index 00000000..008bebec --- /dev/null +++ b/src/main/gui/gui_main.gd.uid @@ -0,0 +1 @@ +uid://bcwmgn00jhlr0 diff --git a/src/main/main.gd b/src/main/main.gd index 92f5cc1e..5bf42d99 100644 --- a/src/main/main.gd +++ b/src/main/main.gd @@ -17,10 +17,10 @@ func _ready(): pass func _is_cli_mode(args: PackedStringArray) -> bool: - if args.size() > 1 and OS.has_feature("editor"): - return true - elif args.size() >= 1 and OS.has_feature("template"): - return true + var cli_keywords := ["--ghelp", "-gh", "--recent", "-r", "editor", "exec"] + for arg in args: + if cli_keywords.has(arg): + return true return false func _exit(): diff --git a/src/main/main.gd.uid b/src/main/main.gd.uid new file mode 100644 index 00000000..8bf734d9 --- /dev/null +++ b/src/main/main.gd.uid @@ -0,0 +1 @@ +uid://dpnaol0rnve5o diff --git a/src/objects/action.gd.uid b/src/objects/action.gd.uid new file mode 100644 index 00000000..2872ba92 --- /dev/null +++ b/src/objects/action.gd.uid @@ -0,0 +1 @@ +uid://wfk7u12eygxb diff --git a/src/objects/config_file_save_on_set.gd.uid b/src/objects/config_file_save_on_set.gd.uid new file mode 100644 index 00000000..4199d5a1 --- /dev/null +++ b/src/objects/config_file_save_on_set.gd.uid @@ -0,0 +1 @@ +uid://bu8ylgkyxdfqk diff --git a/src/objects/config_file_section.gd.uid b/src/objects/config_file_section.gd.uid new file mode 100644 index 00000000..e6d76d5b --- /dev/null +++ b/src/objects/config_file_section.gd.uid @@ -0,0 +1 @@ +uid://cy01ivevjyby diff --git a/src/objects/config_file_value.gd.uid b/src/objects/config_file_value.gd.uid new file mode 100644 index 00000000..dc2632d6 --- /dev/null +++ b/src/objects/config_file_value.gd.uid @@ -0,0 +1 @@ +uid://dprmn1lger7jv diff --git a/src/objects/custom_commands_popup_items.gd.uid b/src/objects/custom_commands_popup_items.gd.uid new file mode 100644 index 00000000..16534c77 --- /dev/null +++ b/src/objects/custom_commands_popup_items.gd.uid @@ -0,0 +1 @@ +uid://cgol5yf3wbsvw diff --git a/src/objects/node_component/_component.gd.uid b/src/objects/node_component/_component.gd.uid new file mode 100644 index 00000000..ece44785 --- /dev/null +++ b/src/objects/node_component/_component.gd.uid @@ -0,0 +1 @@ +uid://rwuaol2boe5l diff --git a/src/objects/node_component/comp.gd.uid b/src/objects/node_component/comp.gd.uid new file mode 100644 index 00000000..09b2f266 --- /dev/null +++ b/src/objects/node_component/comp.gd.uid @@ -0,0 +1 @@ +uid://cursddf6qcxgu diff --git a/src/objects/node_component/comp_init.gd.uid b/src/objects/node_component/comp_init.gd.uid new file mode 100644 index 00000000..f43915f2 --- /dev/null +++ b/src/objects/node_component/comp_init.gd.uid @@ -0,0 +1 @@ +uid://csc6fcl6bu1w8 diff --git a/src/objects/node_component/comp_refs.gd.uid b/src/objects/node_component/comp_refs.gd.uid new file mode 100644 index 00000000..089e4007 --- /dev/null +++ b/src/objects/node_component/comp_refs.gd.uid @@ -0,0 +1 @@ +uid://blv34t4rda08d diff --git a/src/objects/node_component/comp_scene.gd.uid b/src/objects/node_component/comp_scene.gd.uid new file mode 100644 index 00000000..dcdf3bbf --- /dev/null +++ b/src/objects/node_component/comp_scene.gd.uid @@ -0,0 +1 @@ +uid://btifplcguvm8j diff --git a/src/objects/os_process_schema.gd.uid b/src/objects/os_process_schema.gd.uid new file mode 100644 index 00000000..1582103a --- /dev/null +++ b/src/objects/os_process_schema.gd.uid @@ -0,0 +1 @@ +uid://x161twdo4i7r diff --git a/src/objects/random_project_names.gd.uid b/src/objects/random_project_names.gd.uid new file mode 100644 index 00000000..6050715a --- /dev/null +++ b/src/objects/random_project_names.gd.uid @@ -0,0 +1 @@ +uid://di1rbdpadd2cp diff --git a/src/objects/set.gd.uid b/src/objects/set.gd.uid new file mode 100644 index 00000000..83a4f9fb --- /dev/null +++ b/src/objects/set.gd.uid @@ -0,0 +1 @@ +uid://dveubmjukijpm diff --git a/src/output.gd.uid b/src/output.gd.uid new file mode 100644 index 00000000..295144b1 --- /dev/null +++ b/src/output.gd.uid @@ -0,0 +1 @@ +uid://cwce0pkshfhmk diff --git a/src/services/godots_downloads.gd.uid b/src/services/godots_downloads.gd.uid new file mode 100644 index 00000000..2390ea42 --- /dev/null +++ b/src/services/godots_downloads.gd.uid @@ -0,0 +1 @@ +uid://d24j4lt0eed8u diff --git a/src/services/godots_install.gd.uid b/src/services/godots_install.gd.uid new file mode 100644 index 00000000..a18efe6d --- /dev/null +++ b/src/services/godots_install.gd.uid @@ -0,0 +1 @@ +uid://bhvjk8g5gbeyt diff --git a/src/services/godots_recent_releases.gd.uid b/src/services/godots_recent_releases.gd.uid new file mode 100644 index 00000000..ef35d3de --- /dev/null +++ b/src/services/godots_recent_releases.gd.uid @@ -0,0 +1 @@ +uid://cprvs537u6w13 diff --git a/src/services/godots_releases.gd.uid b/src/services/godots_releases.gd.uid new file mode 100644 index 00000000..3149a89f --- /dev/null +++ b/src/services/godots_releases.gd.uid @@ -0,0 +1 @@ +uid://ca1vq1is4vtcl diff --git a/src/services/local_editors.gd.uid b/src/services/local_editors.gd.uid new file mode 100644 index 00000000..20750f52 --- /dev/null +++ b/src/services/local_editors.gd.uid @@ -0,0 +1 @@ +uid://bu3dhjaebkg2j diff --git a/src/services/projects.gd.uid b/src/services/projects.gd.uid new file mode 100644 index 00000000..415bd8d2 --- /dev/null +++ b/src/services/projects.gd.uid @@ -0,0 +1 @@ +uid://c0wklqjt7udsw diff --git a/src/services/remote_image_src.gd.uid b/src/services/remote_image_src.gd.uid new file mode 100644 index 00000000..8539d940 --- /dev/null +++ b/src/services/remote_image_src.gd.uid @@ -0,0 +1 @@ +uid://dywmvpuacrbrq diff --git a/src/services/version_hint.gd.uid b/src/services/version_hint.gd.uid new file mode 100644 index 00000000..43b15fae --- /dev/null +++ b/src/services/version_hint.gd.uid @@ -0,0 +1 @@ +uid://bln863jlxrnpu diff --git a/src/utils.gd.uid b/src/utils.gd.uid new file mode 100644 index 00000000..ca7884f2 --- /dev/null +++ b/src/utils.gd.uid @@ -0,0 +1 @@ +uid://bhi1eu4lsg4nj diff --git a/tests/cases/cli/parser/parser_not_ok.gd.uid b/tests/cases/cli/parser/parser_not_ok.gd.uid new file mode 100644 index 00000000..85a529a6 --- /dev/null +++ b/tests/cases/cli/parser/parser_not_ok.gd.uid @@ -0,0 +1 @@ +uid://cxfw6ml60n7t5 diff --git a/tests/cases/cli/parser/parser_ok.gd.uid b/tests/cases/cli/parser/parser_ok.gd.uid new file mode 100644 index 00000000..7e20cdae --- /dev/null +++ b/tests/cases/cli/parser/parser_ok.gd.uid @@ -0,0 +1 @@ +uid://dvf8u0x5ta1n7 diff --git a/tests/cases/cli/parser/test_grammar.gd.uid b/tests/cases/cli/parser/test_grammar.gd.uid new file mode 100644 index 00000000..9729eaef --- /dev/null +++ b/tests/cases/cli/parser/test_grammar.gd.uid @@ -0,0 +1 @@ +uid://b2rup32qy4flr diff --git a/tests/cases/dir_extensions/list_dir_recursive/list_dir_test.gd.uid b/tests/cases/dir_extensions/list_dir_recursive/list_dir_test.gd.uid new file mode 100644 index 00000000..ba2cdb64 --- /dev/null +++ b/tests/cases/dir_extensions/list_dir_recursive/list_dir_test.gd.uid @@ -0,0 +1 @@ +uid://b2jhur5jq1jrj diff --git a/tests/cases/services/local_editor_tests.gd.uid b/tests/cases/services/local_editor_tests.gd.uid new file mode 100644 index 00000000..d49b3c41 --- /dev/null +++ b/tests/cases/services/local_editor_tests.gd.uid @@ -0,0 +1 @@ +uid://joq8y3fgx0dt diff --git a/theme/fill_icons_registry.gd.uid b/theme/fill_icons_registry.gd.uid new file mode 100644 index 00000000..9eff53a0 --- /dev/null +++ b/theme/fill_icons_registry.gd.uid @@ -0,0 +1 @@ +uid://o2qtu74d4yvq diff --git a/theme/fonts/DroidSansFallback.woff2.import b/theme/fonts/DroidSansFallback.woff2.import index 4161f738..e5eefa0f 100644 --- a/theme/fonts/DroidSansFallback.woff2.import +++ b/theme/fonts/DroidSansFallback.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/DroidSansFallback.woff2-9a6b197d8ae3dcc2460b4 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/DroidSansJapanese.woff2.import b/theme/fonts/DroidSansJapanese.woff2.import index f241973c..716176f2 100644 --- a/theme/fonts/DroidSansJapanese.woff2.import +++ b/theme/fonts/DroidSansJapanese.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/DroidSansJapanese.woff2-265c43a1bf482bf9eacbd Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/JetBrainsMono_Regular.woff2.import b/theme/fonts/JetBrainsMono_Regular.woff2.import index a36b2fc1..b7e97493 100644 --- a/theme/fonts/JetBrainsMono_Regular.woff2.import +++ b/theme/fonts/JetBrainsMono_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/JetBrainsMono_Regular.woff2-99ded7c951c024f99 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoNaskhArabicUI_Bold.woff2.import b/theme/fonts/NotoNaskhArabicUI_Bold.woff2.import index 9da901be..060cbc34 100644 --- a/theme/fonts/NotoNaskhArabicUI_Bold.woff2.import +++ b/theme/fonts/NotoNaskhArabicUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoNaskhArabicUI_Bold.woff2-520684f0ef3fa41c Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoNaskhArabicUI_Regular.woff2.import b/theme/fonts/NotoNaskhArabicUI_Regular.woff2.import index 1142ce1d..f6b6dc73 100644 --- a/theme/fonts/NotoNaskhArabicUI_Regular.woff2.import +++ b/theme/fonts/NotoNaskhArabicUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoNaskhArabicUI_Regular.woff2-d202fbf2a7f20 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansBengaliUI_Bold.woff2.import b/theme/fonts/NotoSansBengaliUI_Bold.woff2.import index 757652e7..4ad56694 100644 --- a/theme/fonts/NotoSansBengaliUI_Bold.woff2.import +++ b/theme/fonts/NotoSansBengaliUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansBengaliUI_Bold.woff2-c28c13f059bd163e Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansBengaliUI_Regular.woff2.import b/theme/fonts/NotoSansBengaliUI_Regular.woff2.import index d7308147..e90767d5 100644 --- a/theme/fonts/NotoSansBengaliUI_Regular.woff2.import +++ b/theme/fonts/NotoSansBengaliUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansBengaliUI_Regular.woff2-02d737ecfea93 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansDevanagariUI_Bold.woff2.import b/theme/fonts/NotoSansDevanagariUI_Bold.woff2.import index d4ae9ca7..55218f25 100644 --- a/theme/fonts/NotoSansDevanagariUI_Bold.woff2.import +++ b/theme/fonts/NotoSansDevanagariUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansDevanagariUI_Bold.woff2-63ee35eaa15fa Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansDevanagariUI_Regular.woff2.import b/theme/fonts/NotoSansDevanagariUI_Regular.woff2.import index 1f7f4087..e7f37f76 100644 --- a/theme/fonts/NotoSansDevanagariUI_Regular.woff2.import +++ b/theme/fonts/NotoSansDevanagariUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansDevanagariUI_Regular.woff2-fb318440d8 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansGeorgian_Bold.woff2.import b/theme/fonts/NotoSansGeorgian_Bold.woff2.import index 4769f096..83e5fc71 100644 --- a/theme/fonts/NotoSansGeorgian_Bold.woff2.import +++ b/theme/fonts/NotoSansGeorgian_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansGeorgian_Bold.woff2-52cb92437cabb28b6 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansGeorgian_Regular.woff2.import b/theme/fonts/NotoSansGeorgian_Regular.woff2.import index 07870f1d..5909c2bd 100644 --- a/theme/fonts/NotoSansGeorgian_Regular.woff2.import +++ b/theme/fonts/NotoSansGeorgian_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansGeorgian_Regular.woff2-46111eb867e12b Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansHebrew_Bold.woff2.import b/theme/fonts/NotoSansHebrew_Bold.woff2.import index 58168196..1cfd3eeb 100644 --- a/theme/fonts/NotoSansHebrew_Bold.woff2.import +++ b/theme/fonts/NotoSansHebrew_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansHebrew_Bold.woff2-f617b2c57cf9e22ce25 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansHebrew_Regular.woff2.import b/theme/fonts/NotoSansHebrew_Regular.woff2.import index 932ed57d..d449a3f1 100644 --- a/theme/fonts/NotoSansHebrew_Regular.woff2.import +++ b/theme/fonts/NotoSansHebrew_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansHebrew_Regular.woff2-848ab38e7684cb22 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansMalayalamUI_Bold.woff2.import b/theme/fonts/NotoSansMalayalamUI_Bold.woff2.import index 6a1057e2..2d0333c8 100644 --- a/theme/fonts/NotoSansMalayalamUI_Bold.woff2.import +++ b/theme/fonts/NotoSansMalayalamUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansMalayalamUI_Bold.woff2-9d7e46bc593a52 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansMalayalamUI_Regular.woff2.import b/theme/fonts/NotoSansMalayalamUI_Regular.woff2.import index 494ceee0..bc3551fc 100644 --- a/theme/fonts/NotoSansMalayalamUI_Regular.woff2.import +++ b/theme/fonts/NotoSansMalayalamUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansMalayalamUI_Regular.woff2-e5d1120be62 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansOriyaUI_Bold.woff2.import b/theme/fonts/NotoSansOriyaUI_Bold.woff2.import index 67fef401..b1b867b5 100644 --- a/theme/fonts/NotoSansOriyaUI_Bold.woff2.import +++ b/theme/fonts/NotoSansOriyaUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansOriyaUI_Bold.woff2-83f4642ca095fba343 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansOriyaUI_Regular.woff2.import b/theme/fonts/NotoSansOriyaUI_Regular.woff2.import index e11712f3..92342ddb 100644 --- a/theme/fonts/NotoSansOriyaUI_Regular.woff2.import +++ b/theme/fonts/NotoSansOriyaUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansOriyaUI_Regular.woff2-91e03b48e680f66 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansSinhalaUI_Bold.woff2.import b/theme/fonts/NotoSansSinhalaUI_Bold.woff2.import index 0d3559a3..934b2b0d 100644 --- a/theme/fonts/NotoSansSinhalaUI_Bold.woff2.import +++ b/theme/fonts/NotoSansSinhalaUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansSinhalaUI_Bold.woff2-dabd79732e74b0e3 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansSinhalaUI_Regular.woff2.import b/theme/fonts/NotoSansSinhalaUI_Regular.woff2.import index 5caf08e9..f2c480d8 100644 --- a/theme/fonts/NotoSansSinhalaUI_Regular.woff2.import +++ b/theme/fonts/NotoSansSinhalaUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansSinhalaUI_Regular.woff2-dee29c2d56928 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansTamilUI_Bold.woff2.import b/theme/fonts/NotoSansTamilUI_Bold.woff2.import index ea738902..9d655a04 100644 --- a/theme/fonts/NotoSansTamilUI_Bold.woff2.import +++ b/theme/fonts/NotoSansTamilUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansTamilUI_Bold.woff2-4d801e2369450e24be Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansTamilUI_Regular.woff2.import b/theme/fonts/NotoSansTamilUI_Regular.woff2.import index 65072616..329bb41e 100644 --- a/theme/fonts/NotoSansTamilUI_Regular.woff2.import +++ b/theme/fonts/NotoSansTamilUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansTamilUI_Regular.woff2-ba01f8e41fbaeb8 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansTeluguUI_Bold.woff2.import b/theme/fonts/NotoSansTeluguUI_Bold.woff2.import index 2dd64306..7cfb7664 100644 --- a/theme/fonts/NotoSansTeluguUI_Bold.woff2.import +++ b/theme/fonts/NotoSansTeluguUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansTeluguUI_Bold.woff2-d3c4287ef66852542 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansTeluguUI_Regular.woff2.import b/theme/fonts/NotoSansTeluguUI_Regular.woff2.import index 62e51915..b61db151 100644 --- a/theme/fonts/NotoSansTeluguUI_Regular.woff2.import +++ b/theme/fonts/NotoSansTeluguUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansTeluguUI_Regular.woff2-9a88ead9abdf6e Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansThaiUI_Bold.woff2.import b/theme/fonts/NotoSansThaiUI_Bold.woff2.import index fb3fc6b7..1ad0358f 100644 --- a/theme/fonts/NotoSansThaiUI_Bold.woff2.import +++ b/theme/fonts/NotoSansThaiUI_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansThaiUI_Bold.woff2-b690a7b4b7cb0776e50 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSansThaiUI_Regular.woff2.import b/theme/fonts/NotoSansThaiUI_Regular.woff2.import index cbaa42dd..143a5230 100644 --- a/theme/fonts/NotoSansThaiUI_Regular.woff2.import +++ b/theme/fonts/NotoSansThaiUI_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSansThaiUI_Regular.woff2-96c195d60c3cf9de Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSans_Bold.woff2.import b/theme/fonts/NotoSans_Bold.woff2.import index 04d937fe..22a44d2a 100644 --- a/theme/fonts/NotoSans_Bold.woff2.import +++ b/theme/fonts/NotoSans_Bold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSans_Bold.woff2-a8093cd7cacca575b65175146 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/NotoSans_Regular.woff2.import b/theme/fonts/NotoSans_Regular.woff2.import index e2b7fa0a..c279fa99 100644 --- a/theme/fonts/NotoSans_Regular.woff2.import +++ b/theme/fonts/NotoSans_Regular.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/NotoSans_Regular.woff2-a8a0deb4c3becc1fa669e5 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/fonts/OpenSans_SemiBold.woff2.import b/theme/fonts/OpenSans_SemiBold.woff2.import index 2bafac31..365845e2 100644 --- a/theme/fonts/OpenSans_SemiBold.woff2.import +++ b/theme/fonts/OpenSans_SemiBold.woff2.import @@ -15,13 +15,16 @@ dest_files=["res://.godot/imported/OpenSans_SemiBold.woff2-e93c4ca78d113723f4013 Rendering=null antialiasing=1 generate_mipmaps=false +disable_embedded_bitmaps=true multichannel_signed_distance_field=false msdf_pixel_range=8 msdf_size=48 allow_system_fallback=true force_autohinter=false +modulate_color_glyphs=false hinting=1 subpixel_positioning=1 +keep_rounding_remainders=true oversampling=0.0 Fallbacks=null fallbacks=[] diff --git a/theme/theme.gd.uid b/theme/theme.gd.uid new file mode 100644 index 00000000..5cb532a9 --- /dev/null +++ b/theme/theme.gd.uid @@ -0,0 +1 @@ +uid://du2rpubf8yhv From cfce552712c491713b215ea98556a934278111ce Mon Sep 17 00:00:00 2001 From: POWERHACK <74624111+POWERHACK69@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:28:44 +0100 Subject: [PATCH 3/6] fix GUI indentation --- project.godot | 2 -- src/components/rss_feed/rss_feed.gd | 4 ++-- src/main/gui/gui_main.gd | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/project.godot b/project.godot index 83e3b945..4109f5b3 100644 --- a/project.godot +++ b/project.godot @@ -19,7 +19,6 @@ config/description="Ultimate go-to hub for managing your Godot versions and proj config/tags=PackedStringArray("application") run/main_scene="res://src/main/main.tscn" config/features=PackedStringArray("4.6", "GL Compatibility") - run/low_processor_mode=true boot_splash/bg_color=Color(0.113725, 0.133333, 0.160784, 1) config/icon="res://icon.svg" @@ -54,7 +53,6 @@ window/subwindows/embed_subwindows=false enabled=PackedStringArray("res://addons/use-context/plugin.cfg", "res://addons/gdUnit4/plugin.cfg") - [filesystem] import/blender/enabled=false diff --git a/src/components/rss_feed/rss_feed.gd b/src/components/rss_feed/rss_feed.gd index 38191886..5fc43e41 100644 --- a/src/components/rss_feed/rss_feed.gd +++ b/src/components/rss_feed/rss_feed.gd @@ -68,7 +68,7 @@ func _refetch_data() -> void: func _fetch_feed(source: Dictionary) -> Array: var headers := PackedStringArray([Config.AGENT_HEADER]) - _http_request.request(source.url, headers, HTTPClient.METHOD_GET) + _http_request.request(source.url as String, headers, HTTPClient.METHOD_GET) var response: Array = await _http_request.request_completed var response_obj := HttpClient.Response.new(response) @@ -87,7 +87,7 @@ func _parse_feed(root: XMLNode, source: Dictionary) -> Array: # YouTube (Atom) uses , standard RSS uses for child: XMLNode in root.children: if child.name == "entry" or child.name == "item": - var item := _parse_entry(child, source.name) + var item := _parse_entry(child, source.name as String) if item and not item.title.is_empty(): items.append(item) return items diff --git a/src/main/gui/gui_main.gd b/src/main/gui/gui_main.gd index 85ac3b44..053f6cfb 100644 --- a/src/main/gui/gui_main.gd +++ b/src/main/gui/gui_main.gd @@ -168,9 +168,9 @@ func _enter_tree() -> void: DisplayServer.window_set_size(window_size) if screen_rect.size != Vector2i(): - var window_position := Vector2i( - screen_rect.position.x + (screen_rect.size.x - window_size.x) / 2, - screen_rect.position.y + (screen_rect.size.y - window_size.y) / 2 + var window_position := Vector2i( + screen_rect.position.x + (screen_rect.size.x - window_size.x) / 2, + screen_rect.position.y + (screen_rect.size.y - window_size.y) / 2 ) DisplayServer.window_set_position(window_position) From 6df643ff5fde3bf7ef2717c8bdc66a096dff50e2 Mon Sep 17 00:00:00 2001 From: POWERHACK <74624111+POWERHACK69@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:43:50 +0100 Subject: [PATCH 4/6] Added thumbnail support --- src/components/rss_feed/list/rss_feed_item.gd | 21 +++++++++++++++++++ .../rss_feed/list/rss_feed_item.tscn | 9 +++++++- src/components/rss_feed/list/rss_feed_list.gd | 6 +++++- src/components/rss_feed/rss_feed.gd | 5 +++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/components/rss_feed/list/rss_feed_item.gd b/src/components/rss_feed/list/rss_feed_item.gd index 1af07840..24f1cf95 100644 --- a/src/components/rss_feed/list/rss_feed_item.gd +++ b/src/components/rss_feed/list/rss_feed_item.gd @@ -7,9 +7,11 @@ signal tag_clicked(tag: String) @onready var _source_label := %SourceLabel as Label @onready var _date_label := %DateLabel as Label @onready var _open_button := %OpenButton as Button +@onready var _thumbnail := %Thumbnail as TextureRect var _item: RssFeed.FeedItem var _tags: Array = [] +var _images_src: RemoteImageSrc.I func init(item: RssFeed.FeedItem) -> void: @@ -23,6 +25,25 @@ func init(item: RssFeed.FeedItem) -> void: _open_button.icon = get_theme_icon("ExternalLink", "EditorIcons") _open_button.pressed.connect(func() -> void: OS.shell_open(item.link)) + if not item.thumbnail_url.is_empty(): + _load_thumbnail() + + +func set_images_src(images_src: RemoteImageSrc.I) -> void: + _images_src = images_src + if _item and not _item.thumbnail_url.is_empty(): + _load_thumbnail() + + +func _load_thumbnail() -> void: + if _images_src == null or _item.thumbnail_url.is_empty(): + return + _images_src.async_load_img(_item.thumbnail_url, func(tex: Texture2D) -> void: + if tex is ImageTexture: + (tex as ImageTexture).set_size_override(Vector2i(64, 64) * Config.EDSCALE) + _thumbnail.texture = tex + ) + func apply_filter(filter: Callable) -> bool: return filter.call({ diff --git a/src/components/rss_feed/list/rss_feed_item.tscn b/src/components/rss_feed/list/rss_feed_item.tscn index 0ab8de80..6c94760a 100644 --- a/src/components/rss_feed/list/rss_feed_item.tscn +++ b/src/components/rss_feed/list/rss_feed_item.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=5 format=3] +[gd_scene load_steps=6 format=3] [ext_resource type="Script" path="res://src/components/rss_feed/list/rss_feed_item.gd" id="1_script"] [ext_resource type="Script" path="res://src/components/misc/list_item_title_label.gd" id="2_title"] [ext_resource type="Script" path="res://src/components/misc/themed_button.gd" id="3_button"] +[ext_resource type="Script" path="res://src/components/misc/list_item_icon.gd" id="4_icon"] [node name="RssFeedItem" type="HBoxContainer"] anchors_preset = 15 @@ -12,6 +13,12 @@ grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_script") +[node name="Thumbnail" type="TextureRect" parent="."] +unique_name_in_owner = true +layout_mode = 2 +script = ExtResource("4_icon") +_stretch_mode = 2 + [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 size_flags_horizontal = 3 diff --git a/src/components/rss_feed/list/rss_feed_list.gd b/src/components/rss_feed/list/rss_feed_list.gd index e9be614b..4f221025 100644 --- a/src/components/rss_feed/list/rss_feed_list.gd +++ b/src/components/rss_feed/list/rss_feed_list.gd @@ -2,7 +2,11 @@ extends VBoxList func _post_add(_item_data: Object, _raw_item_control: Control) -> void: - pass + if _raw_item_control is RssFeedItemControl: + var rss_control := get_parent() + if rss_control is RssFeedControl: + var item := _raw_item_control as RssFeedItemControl + item.set_images_src((rss_control as RssFeedControl)._images_src) func _item_comparator(a: Dictionary, b: Dictionary) -> bool: diff --git a/src/components/rss_feed/rss_feed.gd b/src/components/rss_feed/rss_feed.gd index 5fc43e41..fb251c24 100644 --- a/src/components/rss_feed/rss_feed.gd +++ b/src/components/rss_feed/rss_feed.gd @@ -30,6 +30,7 @@ const FEED_SOURCES: Array[Dictionary] = [ var _http_request: HTTPRequest var _data_loaded := false var _fetching := false +var _images_src: RemoteImageSrc.I func _ready() -> void: @@ -37,6 +38,10 @@ func _ready() -> void: add_child(_http_request) _refresh_button.icon = get_theme_icon("Reload", "EditorIcons") _refresh_button.pressed.connect(_refetch_data) + _images_src = RemoteImageSrc.LoadFileBuffer.new( + RemoteImageSrc.FileByUrlCachedEtag.new(), + get_theme_icon("FileBrokenBigThumb", "EditorIcons") + ) func init() -> void: From d3346a023b3e83655cab14b3b916fe3e99a00759 Mon Sep 17 00:00:00 2001 From: POWERHACK <74624111+POWERHACK69@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:46:22 +0100 Subject: [PATCH 5/6] Add default thumbnail for RSS feed items When an RSS feed item doesn't have a thumbnail URL, display a default project icon instead of leaving the thumbnail empty. The thumbnail loading logic was restructured to check the empty case first for improved clarity. --- src/components/rss_feed/list/rss_feed_item.gd | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/rss_feed/list/rss_feed_item.gd b/src/components/rss_feed/list/rss_feed_item.gd index 24f1cf95..c7c05aac 100644 --- a/src/components/rss_feed/list/rss_feed_item.gd +++ b/src/components/rss_feed/list/rss_feed_item.gd @@ -14,6 +14,9 @@ var _tags: Array = [] var _images_src: RemoteImageSrc.I +const DEFAULT_ICON = preload("res://assets/default_project_icon.svg") + + func init(item: RssFeed.FeedItem) -> void: _item = item _tags = [item.source_name] @@ -25,7 +28,9 @@ func init(item: RssFeed.FeedItem) -> void: _open_button.icon = get_theme_icon("ExternalLink", "EditorIcons") _open_button.pressed.connect(func() -> void: OS.shell_open(item.link)) - if not item.thumbnail_url.is_empty(): + if item.thumbnail_url.is_empty(): + _thumbnail.texture = DEFAULT_ICON + else: _load_thumbnail() From ef18c22b8924a9373efa5203b547dbc32c0a2266 Mon Sep 17 00:00:00 2001 From: Michael Azubuike Date: Wed, 22 Jul 2026 20:21:39 +0100 Subject: [PATCH 6/6] feat: add project templates tab for saving and opening project templates - Add ProjectTemplates service (List, Item) backed by ConfigFile + zip storage - Add Templates tab with search, sort, and filter support - Add Save Template dialog to save any project as a template (name, description, source) - Add Open Template dialog to create new projects from templates (name, target path) - Add Import Template to import .zip template files from disk - Add 'Save as Template' action to project item context menu - Templates stored as .zip archives in user://templates/ with metadata in user://templates.cfg --- .../project_templates/open_template_dialog.gd | 135 ++++++++++++ .../open_template_dialog.tscn | 65 ++++++ .../project_templates/project_templates.gd | 194 ++++++++++++++++++ .../project_templates/project_templates.tscn | 42 ++++ .../project_templates_list.gd | 29 +++ .../project_templates_list.tscn | 17 ++ .../project_templates/save_template_dialog.gd | 83 ++++++++ .../save_template_dialog.tscn | 72 +++++++ .../project_templates/template_list_item.gd | 62 ++++++ .../project_templates/template_list_item.tscn | 73 +++++++ .../projects/project_item/project_item.gd | 91 +++++++- src/config.gd | 2 + src/main/gui/gui_main.gd | 12 ++ src/main/gui/gui_main.tscn | 10 +- src/services/project_templates.gd | 133 ++++++++++++ 15 files changed, 1017 insertions(+), 3 deletions(-) create mode 100644 src/components/project_templates/open_template_dialog.gd create mode 100644 src/components/project_templates/open_template_dialog.tscn create mode 100644 src/components/project_templates/project_templates.gd create mode 100644 src/components/project_templates/project_templates.tscn create mode 100644 src/components/project_templates/project_templates_list.gd create mode 100644 src/components/project_templates/project_templates_list.tscn create mode 100644 src/components/project_templates/save_template_dialog.gd create mode 100644 src/components/project_templates/save_template_dialog.tscn create mode 100644 src/components/project_templates/template_list_item.gd create mode 100644 src/components/project_templates/template_list_item.tscn create mode 100644 src/services/project_templates.gd diff --git a/src/components/project_templates/open_template_dialog.gd b/src/components/project_templates/open_template_dialog.gd new file mode 100644 index 00000000..ba87e06d --- /dev/null +++ b/src/components/project_templates/open_template_dialog.gd @@ -0,0 +1,135 @@ +class_name OpenTemplateDialog +extends ConfirmationDialog + +signal opened(project_name: String, target_path: String, template_item: ProjectTemplates.Item) + +@onready var _project_name_edit: LineEdit = %ProjectNameEdit +@onready var _project_path_edit: LineEdit = %ProjectPathLineEdit +@onready var _browse_path_button: Button = %BrowsePathButton +@onready var _create_folder_check: CheckButton = %CreateFolderCheck +@onready var _message_label: Label = %MessageLabel +@onready var _status_rect: TextureRect = %StatusRect +@onready var _file_dialog: FileDialog = $FileDialog + +var _template_item: ProjectTemplates.Item +var _auto_dir: String = "" + + +func _ready() -> void: + dialog_hide_on_ok = false + _browse_path_button.icon = get_theme_icon("Load", "EditorIcons") + + _project_name_edit.text_changed.connect(func(_arg: String) -> void: + _update_project_dir() + _validate() + ) + _project_path_edit.text_changed.connect(func(_arg: String) -> void: _validate()) + + _browse_path_button.pressed.connect(func() -> void: + var path := _project_path_edit.text.strip_edges() + if _create_folder_check.button_pressed: + _file_dialog.current_dir = path.get_base_dir() + else: + _file_dialog.current_dir = path + _file_dialog.popup_centered_ratio(0.5) + ) + + _file_dialog.dir_selected.connect(func(dir: String) -> void: + if _create_folder_check.button_pressed: + var folder := _project_path_edit.text.get_file() + if folder != _auto_dir: + folder = _auto_dir + _project_path_edit.text = dir.path_join(folder) + else: + _project_path_edit.text = dir + _validate() + ) + + _create_folder_check.toggled.connect(func(pressed: bool) -> void: + var path := _project_path_edit.text + if pressed: + path = path.path_join(_auto_dir) + else: + path = path.rstrip("/\\") + if path.get_file() == _auto_dir: + pass + else: + pass + path = path.get_base_dir() + _project_path_edit.text = path + _validate() + ) + + confirmed.connect(func() -> void: + var project_name := _project_name_edit.text.strip_edges() + var target_path := _project_path_edit.text.strip_edges() + + if _create_folder_check.button_pressed: + DirAccess.make_dir_recursive_absolute(target_path) + + opened.emit(project_name, target_path, _template_item) + hide() + ) + + min_size = Vector2(640, 200) * Config.EDSCALE + + +func raise(template_item: ProjectTemplates.Item) -> void: + _template_item = template_item + _project_name_edit.text = template_item.name + _project_path_edit.text = Config.DEFAULT_PROJECTS_PATH.ret() + _auto_dir = "" + _validate() + popup_centered() + _project_name_edit.grab_focus() + _project_name_edit.select_all() + _update_project_dir() + + +func _update_project_dir() -> void: + var project_name := _project_name_edit.text.strip_edges() + var new_auto_dir := project_name.to_snake_case().validate_filename() + if _create_folder_check.button_pressed: + var path := _project_path_edit.text + if path.get_file() == _auto_dir or _auto_dir.is_empty(): + _project_path_edit.text = path.get_base_dir().path_join(new_auto_dir) + _auto_dir = new_auto_dir + _validate() + + +func _validate() -> void: + var project_name := _project_name_edit.text.strip_edges() + var path := _project_path_edit.text.strip_edges() + + if project_name.is_empty(): + _error(tr("Project name cannot be blank.")) + return + + if path.is_empty(): + _error(tr("Target path cannot be blank.")) + return + + if _create_folder_check.button_pressed: + _success(tr("The project folder will be automatically created.")) + return + + var dir := DirAccess.open(path) + if not dir: + _error(tr("The path specified doesn't exist.")) + return + + _success(tr("Ready to create project from template.")) + + +func _error(text: String) -> void: + _message_label.text = text + _message_label.add_theme_color_override("font_color", get_theme_color("error_color", "Editor")) + _status_rect.texture = get_theme_icon("StatusError", "EditorIcons") + get_ok_button().disabled = true + + +func _success(text: String) -> void: + _message_label.text = text + _message_label.add_theme_color_override("font_color", get_theme_color("success_color", "Editor")) + _status_rect.texture = get_theme_icon("StatusSuccess", "EditorIcons") + get_ok_button().disabled = false diff --git a/src/components/project_templates/open_template_dialog.tscn b/src/components/project_templates/open_template_dialog.tscn new file mode 100644 index 00000000..03a9c05e --- /dev/null +++ b/src/components/project_templates/open_template_dialog.tscn @@ -0,0 +1,65 @@ +[gd_scene load_steps=4 format=3] + +[ext_resource type="Script" path="res://src/components/project_templates/open_template_dialog.gd" id="1_script"] + +[node name="OpenTemplateDialog" type="ConfirmationDialog"] +title = "Create Project from Template" +ok_button_text = "Create" +script = ExtResource("1_script") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="NameLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Project Name:" + +[node name="ProjectNameEdit" type="LineEdit" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "My New Project" + +[node name="PathLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Target Path:" + +[node name="PathHBox" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="ProjectPathLineEdit" type="LineEdit" parent="VBoxContainer/PathHBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="BrowsePathButton" type="Button" parent="VBoxContainer/PathHBox"] +unique_name_in_owner = true +layout_mode = 2 +text = "Browse" + +[node name="CreateFolderCheck" type="CheckButton" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Create project folder" + +[node name="MessageHBox" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="StatusRect" type="TextureRect" parent="VBoxContainer/MessageHBox"] +unique_name_in_owner = true +layout_mode = 2 +custom_minimum_size = Vector2(16, 16) +stretch_mode = 5 + +[node name="MessageLabel" type="Label" parent="VBoxContainer/MessageHBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "" + +[node name="FileDialog" type="FileDialog" parent="."] +title = "Select Target Directory" +ok_button_text = "Select" +file_mode = 2 +access = 2 diff --git a/src/components/project_templates/project_templates.gd b/src/components/project_templates/project_templates.gd new file mode 100644 index 00000000..f5a79e8c --- /dev/null +++ b/src/components/project_templates/project_templates.gd @@ -0,0 +1,194 @@ +class_name ProjectTemplatesControl +extends HBoxContainer + +@onready var _sidebar: ActionsSidebarControl = %ActionsSidebar +@onready var _templates_list: ProjectTemplatesVBoxList = %ProjectTemplatesList +@onready var _save_template_dialog: SaveTemplateDialog = %SaveTemplateDialog +@onready var _open_template_dialog: OpenTemplateDialog = %OpenTemplateDialog + +var _templates: ProjectTemplates.List +var _projects_service: Projects.List + + +func init(templates: ProjectTemplates.List, projects_service: Projects.List) -> void: + _templates = templates + _projects_service = projects_service + + var actions := Action.List.new([ + Action.from_dict({ + "key": "save-template", + "icon": Action.IconTheme.new(self, "Save", "EditorIcons"), + "act": func() -> void: _save_template_dialog.raise(), + "label": tr("Save as Template"), + }), + Action.from_dict({ + "key": "import-template", + "icon": Action.IconTheme.new(self, "Load", "EditorIcons"), + "act": _import_template, + "label": tr("Import Template"), + }), + Action.from_dict({ + "key": "refresh", + "icon": Action.IconTheme.new(self, "Reload", "EditorIcons"), + "act": _refresh, + "label": tr("Refresh List"), + }) + ]) + + $ProjectTemplatesList/HBoxContainer.add_child(actions.by_key('refresh').to_btn().make_flat(true).show_text(false)) + $ProjectTemplatesList/HBoxContainer.add_child(actions.by_key('save-template').to_btn().make_flat(true).show_text(false)) + $ProjectTemplatesList/HBoxContainer.add_child(actions.by_key('import-template').to_btn().make_flat(true).show_text(false)) + + _save_template_dialog.saved.connect(_on_save_template) + _open_template_dialog.opened.connect(_on_open_template) + + _templates_list.refresh(_templates.all()) + _load_templates() + + +func _load_templates() -> void: + for template: ProjectTemplates.Item in _templates.all(): + template.loaded.connect(func() -> void: + _templates_list.sort_items() + ) + _templates_list.sort_items() + _templates_list.update_filters() + + +func _refresh() -> void: + _templates.load() + _templates_list.refresh(_templates.all()) + _load_templates() + + +func _on_save_template(template_name: String, description: String, source_path: String, tags: Array) -> void: + var template := _templates.add(template_name, description, source_path, tags) + _templates.save() + + # Create the zip from the source project + var project_dir := source_path.get_base_dir() + var err := _create_template_zip(project_dir, template.zip_path) + if err != OK: + Output.push("Failed to create template zip: %s" % err) + return + + _templates_list.add(template) + _templates_list.sort_items() + + +func _create_template_zip(source_dir: String, target_zip_path: String) -> Error: + # Use zip command to create archive from source directory + var output := [] + var exit_code: int + + # Ensure target directory exists + DirAccess.make_dir_recursive_absolute(target_zip_path.get_base_dir()) + + # Remove existing zip if present + if FileAccess.file_exists(target_zip_path): + DirAccess.remove_absolute(target_zip_path) + + if OS.has_feature("windows"): + exit_code = OS.execute( + "powershell.exe", + [ + "-command", + "Set-Location '%s'; Compress-Archive -Path '*' -DestinationPath '%s' -Force" % [ + source_dir, + ProjectSettings.globalize_path(target_zip_path) + ] + ], output, true + ) + else: + exit_code = OS.execute( + "bash", + [ + "-c", + "cd '%s' && zip -r '%s' ." % [ + source_dir, + ProjectSettings.globalize_path(target_zip_path) + ] + ], output, true + ) + + Output.push(output.pop_front()) + Output.push("Template zip created with exit code: %s" % exit_code) + return OK if exit_code == 0 else FAILED + + +func _on_open_template(project_name: String, target_path: String, template_item: ProjectTemplates.Item) -> void: + if not template_item.is_zip_valid: + Output.push("Template zip file is missing: %s" % template_item.zip_path) + return + + # Unzip the template to the target path + var zip_reader := ZIPReader.new() + var unzip_err := zip_reader.open(template_item.zip_path) + if unzip_err != OK: + zip_reader.close() + Output.push("Failed to open template zip: %s" % unzip_err) + return + + var unzip_result := zip.unzip_to_path(zip_reader, target_path) + zip_reader.close() + + if unzip_result != OK: + Output.push("Failed to extract template: %s" % unzip_result) + return + + # Find project.godot in the extracted files + var project_configs := utils.find_project_godot_files(target_path) + if len(project_configs) == 0: + Output.push("No project.godot found in extracted template") + return + + # Update the project name in project.godot + var project_file_path := project_configs[0].path + var cfg := ConfigFile.new() + var err := cfg.load(project_file_path) + if not err: + cfg.set_value("application", "config/name", project_name) + cfg.save(project_file_path) + + # Import the project into the projects list + var project := _projects_service.add(project_file_path, "") + project.load() + _projects_service.save() + + _templates_list.sort_items() + + +func _import_template() -> void: + # Import a template from a zip file + var file_dialog := FileDialog.new() + file_dialog.title = "Import Template Zip" + file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE + file_dialog.access = FileDialog.ACCESS_FILESYSTEM + file_dialog.filters = PackedStringArray(["*.zip ; Zip Files"]) + + file_dialog.file_selected.connect(func(path: String) -> void: + var template_name := path.get_file().replace(".zip", "").capitalize() + var template := _templates.add(template_name, "Imported template", "", []) + _templates.save() + + # Copy the zip file to the templates directory + var err := DirAccess.copy_absolute(path, template.zip_path) + if err != OK: + Output.push("Failed to copy template zip: %s" % err) + return + + _templates_list.add(template) + _templates_list.sort_items() + ) + + add_child(file_dialog) + file_dialog.popup_centered_ratio(0.5) + + +func _on_templates_list_item_selected(item: TemplateListItemControl) -> void: + pass + + +func _on_templates_list_item_removed(item_data: ProjectTemplates.Item) -> void: + _templates.erase(item_data.id) + _templates.save() diff --git a/src/components/project_templates/project_templates.tscn b/src/components/project_templates/project_templates.tscn new file mode 100644 index 00000000..1f04baa8 --- /dev/null +++ b/src/components/project_templates/project_templates.tscn @@ -0,0 +1,42 @@ +[gd_scene load_steps=7 format=3] + +[ext_resource type="PackedScene" uid="uid://cuuiumge42ghh" path="res://src/components/actions_sidebar/actions_sidebar.tscn" id="1_sidebar"] +[ext_resource type="PackedScene" path="res://src/components/project_templates/project_templates_list.tscn" id="2_list"] +[ext_resource type="Script" path="res://src/components/project_templates/project_templates.gd" id="3_script"] +[ext_resource type="PackedScene" path="res://src/components/project_templates/save_template_dialog.tscn" id="4_save"] +[ext_resource type="PackedScene" path="res://src/components/project_templates/open_template_dialog.tscn" id="5_open"] + +[node name="ProjectTemplates" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("3_script") + +[node name="ProjectTemplatesList" parent="." instance=ExtResource("2_list")] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +_search_cache_is_enabled = true +_cached_search_key = "templates" + +[node name="VBoxContainer" parent="ProjectTemplatesList/HBoxContainer2" index="1"] +visible = false + +[node name="ActionsSidebar" parent="ProjectTemplatesList/HBoxContainer2/VBoxContainer/SidebarContainer" index="0" instance=ExtResource("1_sidebar")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="SaveTemplateDialog" parent="." instance=ExtResource("4_save")] +unique_name_in_owner = true +visible = false + +[node name="OpenTemplateDialog" parent="." instance=ExtResource("5_open")] +unique_name_in_owner = true +visible = false + +[connection signal="item_opened" from="ProjectTemplatesList" to="." method="_on_templates_list_item_selected"] + +[editable path="ProjectTemplatesList"] +[editable path="ProjectTemplatesList/HBoxContainer2/VBoxContainer/SidebarContainer/ActionsSidebar"] diff --git a/src/components/project_templates/project_templates_list.gd b/src/components/project_templates/project_templates_list.gd new file mode 100644 index 00000000..4b6f2139 --- /dev/null +++ b/src/components/project_templates/project_templates_list.gd @@ -0,0 +1,29 @@ +class_name ProjectTemplatesVBoxList +extends VBoxList + +signal item_opened(item_data: ProjectTemplates.Item) +signal item_removed(item_data: ProjectTemplates.Item) + + +func _post_add(raw_item_data: Object, raw_item_control: Control) -> void: + var item_data := raw_item_data as ProjectTemplates.Item + var item_control := raw_item_control as TemplateListItemControl + item_control.opened.connect( + func() -> void: item_opened.emit(item_data) + ) + + +func _item_comparator(a: Dictionary, b: Dictionary) -> bool: + match _sort_option_button.selected: + 0: return a.created_at > b.created_at + _: return a.name < b.name + return a.name < b.name + + +func _fill_sort_options(btn: OptionButton) -> void: + btn.add_item(tr("Newest First")) + btn.add_item(tr("Name")) + + var last_checked_sort := Cache.smart_value(self, "last_checked_sort", true) + btn.select(last_checked_sort.ret(0) as int) + btn.item_selected.connect(func(idx: int) -> void: last_checked_sort.put(idx)) diff --git a/src/components/project_templates/project_templates_list.tscn b/src/components/project_templates/project_templates_list.tscn new file mode 100644 index 00000000..49d1cdf5 --- /dev/null +++ b/src/components/project_templates/project_templates_list.tscn @@ -0,0 +1,17 @@ +[gd_scene load_steps=4 format=3] + +[ext_resource type="PackedScene" uid="uid://bjudiph2xbmbu" path="res://src/components/v_box_list/v_box_list.tscn" id="1_base"] +[ext_resource type="Script" path="res://src/components/project_templates/project_templates_list.gd" id="2_list"] +[ext_resource type="PackedScene" path="res://src/components/project_templates/template_list_item.tscn" id="3_item"] + +[node name="ProjectTemplatesList" instance=ExtResource("1_base")] +script = ExtResource("2_list") +_item_scene = ExtResource("3_item") + +[node name="SearchBox" parent="HBoxContainer" index="1"] +placeholder_text = "Filter Templates" + +[node name="Label" parent="HBoxContainer/HBoxContainer" index="0"] +size_flags_vertical = 1 +horizontal_alignment = 1 +vertical_alignment = 1 diff --git a/src/components/project_templates/save_template_dialog.gd b/src/components/project_templates/save_template_dialog.gd new file mode 100644 index 00000000..50d68792 --- /dev/null +++ b/src/components/project_templates/save_template_dialog.gd @@ -0,0 +1,83 @@ +class_name SaveTemplateDialog +extends ConfirmationDialog + +signal saved(template_name: String, description: String, source_path: String, tags: Array) + +@onready var _template_name_edit: LineEdit = %TemplateNameEdit +@onready var _description_edit: TextEdit = %DescriptionEdit +@onready var _source_path_edit: LineEdit = %SourcePathEdit +@onready var _browse_source_button: Button = %BrowseSourceButton +@onready var _message_label: Label = %MessageLabel +@onready var _status_rect: TextureRect = %StatusRect +@onready var _file_dialog: FileDialog = $FileDialog + + +func _ready() -> void: + dialog_hide_on_ok = false + _browse_source_button.icon = get_theme_icon("Load", "EditorIcons") + + _source_path_edit.text_changed.connect(func(_arg: String) -> void: _validate()) + _template_name_edit.text_changed.connect(func(_arg: String) -> void: _validate()) + + _browse_source_button.pressed.connect(func() -> void: + _file_dialog.current_dir = _source_path_edit.text.strip_edges() + _file_dialog.popup_centered_ratio(0.5) + ) + + _file_dialog.dir_selected.connect(func(dir: String) -> void: + _source_path_edit.text = dir + _validate() + ) + + confirmed.connect(func() -> void: + var template_name := _template_name_edit.text.strip_edges() + var description := _description_edit.text.strip_edges() + var source_path := _source_path_edit.text.strip_edges() + saved.emit(template_name, description, source_path, []) + hide() + ) + + min_size = Vector2(640, 300) * Config.EDSCALE + + +func raise(source_project_path: String = "") -> void: + _template_name_edit.text = "" + _description_edit.text = "" + _source_path_edit.text = source_project_path + _validate() + popup_centered() + _template_name_edit.grab_focus() + + +func _validate() -> void: + var template_name := _template_name_edit.text.strip_edges() + var source_path := _source_path_edit.text.strip_edges() + + if template_name.is_empty(): + _error(tr("Template name cannot be blank.")) + return + + if source_path.is_empty(): + _error(tr("Source project path cannot be blank.")) + return + + var project_file := source_path.path_join("project.godot") + if not FileAccess.file_exists(project_file): + _error(tr("The selected path does not contain a project.godot file.")) + return + + _success(tr("Ready to save template.")) + + +func _error(text: String) -> void: + _message_label.text = text + _message_label.add_theme_color_override("font_color", get_theme_color("error_color", "Editor")) + _status_rect.texture = get_theme_icon("StatusError", "EditorIcons") + get_ok_button().disabled = true + + +func _success(text: String) -> void: + _message_label.text = text + _message_label.add_theme_color_override("font_color", get_theme_color("success_color", "Editor")) + _status_rect.texture = get_theme_icon("StatusSuccess", "EditorIcons") + get_ok_button().disabled = false diff --git a/src/components/project_templates/save_template_dialog.tscn b/src/components/project_templates/save_template_dialog.tscn new file mode 100644 index 00000000..d942930f --- /dev/null +++ b/src/components/project_templates/save_template_dialog.tscn @@ -0,0 +1,72 @@ +[gd_scene load_steps=4 format=3] + +[ext_resource type="Script" path="res://src/components/project_templates/save_template_dialog.gd" id="1_script"] + +[node name="SaveTemplateDialog" type="ConfirmationDialog"] +title = "Save Project as Template" +ok_button_text = "Save" +script = ExtResource("1_script") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="NameLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Template Name:" + +[node name="TemplateNameEdit" type="LineEdit" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "My Awesome Template" + +[node name="DescLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Description:" + +[node name="DescriptionEdit" type="TextEdit" parent="VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 +custom_minimum_size = Vector2(0, 80) +placeholder_text = "Describe what this template includes..." + +[node name="SourceLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Source Project:" + +[node name="SourceHBox" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="SourcePathEdit" type="LineEdit" parent="VBoxContainer/SourceHBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +editable = false + +[node name="BrowseSourceButton" type="Button" parent="VBoxContainer/SourceHBox"] +unique_name_in_owner = true +layout_mode = 2 +text = "Browse" + +[node name="MessageHBox" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="StatusRect" type="TextureRect" parent="VBoxContainer/MessageHBox"] +unique_name_in_owner = true +layout_mode = 2 +custom_minimum_size = Vector2(16, 16) +stretch_mode = 5 + +[node name="MessageLabel" type="Label" parent="VBoxContainer/MessageHBox"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "" + +[node name="FileDialog" type="FileDialog" parent="."] +title = "Select Project Directory" +ok_button_text = "Select" +file_mode = 2 +access = 2 diff --git a/src/components/project_templates/template_list_item.gd b/src/components/project_templates/template_list_item.gd new file mode 100644 index 00000000..9ad43a0a --- /dev/null +++ b/src/components/project_templates/template_list_item.gd @@ -0,0 +1,62 @@ +class_name TemplateListItemControl +extends HBoxListItem + +signal opened +signal removed +signal tag_clicked(tag: String) + +@onready var _title_label: Label = %TitleLabel +@onready var _description_label: Label = %DescriptionLabel +@onready var _date_label: Label = %DateLabel +@onready var _tag_container: ItemTagContainer = %TagContainer +@onready var _icon: TextureRect = $Icon +@onready var _open_button: Button = %OpenButton +@onready var _actions_container: HBoxContainer = %ActionsContainer + +var _template: ProjectTemplates.Item +var _tags := [] +var _sort_data := { + 'ref': self +} + + +func _ready() -> void: + super._ready() + _tag_container.tag_clicked.connect(func(tag: String) -> void: tag_clicked.emit(tag)) + + +func init(item: ProjectTemplates.Item) -> void: + _template = item + _fill_data(item) + + _open_button.pressed.connect(func() -> void: opened.emit()) + + double_clicked.connect(func() -> void: opened.emit()) + + +func _fill_data(item: ProjectTemplates.Item) -> void: + _title_label.text = item.name + _description_label.text = item.description if not item.description.is_empty() else tr("No description") + _date_label.text = item.get_formatted_date() + _icon.texture = get_theme_icon("FileList", "EditorIcons") + _tag_container.set_tags(item.tags) + _tags = item.tags + + if not item.is_zip_valid: + modulate = Color(1, 1, 1, 0.498) + + _sort_data.name = item.name + _sort_data.created_at = item.created_at + _sort_data.tag_sort_string = "".join(item.tags) + + +func apply_filter(filter: Callable) -> bool: + return filter.call({ + 'name': _title_label.text, + 'path': '', + 'tags': _tags + }) + + +func get_sort_data() -> Dictionary: + return _sort_data diff --git a/src/components/project_templates/template_list_item.tscn b/src/components/project_templates/template_list_item.tscn new file mode 100644 index 00000000..5ae553b5 --- /dev/null +++ b/src/components/project_templates/template_list_item.tscn @@ -0,0 +1,73 @@ +[gd_scene load_steps=7 format=3] + +[ext_resource type="Script" path="res://src/components/project_templates/template_list_item.gd" id="1_script"] +[ext_resource type="Script" path="res://src/components/misc/list_item_title_label.gd" id="2_title"] +[ext_resource type="Script" path="res://src/components/misc/list_item_icon.gd" id="3_icon"] +[ext_resource type="Script" path="res://src/components/misc/item_tag_container.gd" id="4_tags"] +[ext_resource type="Script" path="res://src/components/misc/list_item_path_label.gd" id="5_path"] +[ext_resource type="Script" path="res://src/components/misc/themed_button.gd" id="6_btn"] + +[node name="TemplateListItem" type="HBoxContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_script") + +[node name="Icon" type="TextureRect" parent="."] +layout_mode = 2 +script = ExtResource("3_icon") + +[node name="InfoVBox" type="VBoxContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 2 + +[node name="TitleContainer" type="HBoxContainer" parent="InfoVBox"] +layout_mode = 2 + +[node name="TitleLabel" type="Label" parent="InfoVBox/TitleContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Template Name" +text_overrun_behavior = 3 +script = ExtResource("2_title") + +[node name="DateLabel" type="Label" parent="InfoVBox/TitleContainer"] +unique_name_in_owner = true +layout_mode = 2 +modulate = Color(1, 1, 1, 0.498039) +text = "2025-01-01" +script = ExtResource("5_path") + +[node name="ActionsContainer" type="HBoxContainer" parent="InfoVBox/TitleContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="OpenButton" type="Button" parent="InfoVBox/TitleContainer/ActionsContainer"] +unique_name_in_owner = true +layout_mode = 2 +flat = true +script = ExtResource("6_btn") + +[node name="DescriptionContainer" type="HBoxContainer" parent="InfoVBox"] +layout_mode = 2 + +[node name="DescriptionLabel" type="Label" parent="InfoVBox/DescriptionContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "Template description" +text_overrun_behavior = 3 +autowrap_mode = 2 + +[node name="TagsContainer" type="HBoxContainer" parent="InfoVBox"] +layout_mode = 2 + +[node name="TagContainer" type="HBoxContainer" parent="InfoVBox/TagsContainer"] +unique_name_in_owner = true +layout_mode = 2 +script = ExtResource("4_tags") diff --git a/src/components/projects/project_item/project_item.gd b/src/components/projects/project_item/project_item.gd index 25ef8789..c7949212 100644 --- a/src/components/projects/project_item/project_item.gd +++ b/src/components/projects/project_item/project_item.gd @@ -202,6 +202,13 @@ func _fill_actions(item: Projects.Item) -> void: "label": tr("Show in File Manager"), }) + var save_as_template := Action.from_dict({ + "key": "save-as-template", + "icon": Action.IconTheme.new(self, "FileList", "EditorIcons"), + "act": _save_as_template.bind(item), + "label": tr("Save as Template"), + }) + _actions = Action.List.new([ edit, run, @@ -211,6 +218,7 @@ func _fill_actions(item: Projects.Item) -> void: manage_tags, view_command, show_in_file_manager, + save_as_template, remove ]) @@ -240,7 +248,8 @@ func _fill_data(item: Projects.Item) -> void: 'duplicate', 'bind-editor', 'manage-tags', - 'rename' + 'rename', + 'save-as-template' ]).all(): action.disable(item.is_missing) @@ -409,6 +418,86 @@ func _show_in_file_manager(item: Projects.Item) -> void: OS.shell_show_in_file_manager(ProjectSettings.globalize_path(item.path).get_base_dir()) +func _save_as_template(item: Projects.Item) -> void: + var templates_service := Context.use_or_null(self, ProjectTemplates.List) + if templates_service == null: + return + + # Create a simple confirmation dialog + var dialog := ConfirmationDialogAutoFree.new() + dialog.title = tr("Save as Template") + dialog.dialog_text = tr("Save '%s' as a project template?") % item.name + dialog.ok_button_text = tr("Save") + + var vbox := VBoxContainer.new() + dialog.add_child(vbox) + + var name_label := Label.new() + name_label.text = tr("Template Name:") + vbox.add_child(name_label) + + var name_edit := LineEdit.new() + name_edit.text = item.name + " Template" + vbox.add_child(name_edit) + + var desc_label := Label.new() + desc_label.text = tr("Description:") + vbox.add_child(desc_label) + + var desc_edit := TextEdit.new() + desc_edit.custom_minimum_size = Vector2(0, 60) * Config.EDSCALE + vbox.add_child(desc_edit) + + dialog.confirmed.connect(func() -> void: + var template_name := name_edit.text.strip_edges() + var description := desc_edit.text.strip_edges() + var source_path := ProjectSettings.globalize_path(item.path).get_base_dir() + + var template := templates_service.add(template_name, description, source_path, item.tags) + templates_service.save() + + # Create zip from project directory + _create_template_zip(source_path, template.zip_path) + ) + + add_child(dialog) + dialog.popup_centered(Vector2(400, 0) * Config.EDSCALE) + + +func _create_template_zip(source_dir: String, target_zip_path: String) -> void: + var output := [] + var exit_code: int + + DirAccess.make_dir_recursive_absolute(target_zip_path.get_base_dir()) + + if FileAccess.file_exists(target_zip_path): + DirAccess.remove_absolute(target_zip_path) + + # Use cd + zip to create archive from source directory + if OS.has_feature("windows"): + exit_code = OS.execute( + "powershell.exe", + [ + "-command", + "Set-Location '%s'; Compress-Archive -Path '*' -DestinationPath '%s' -Force" % [ + source_dir, + ProjectSettings.globalize_path(target_zip_path) + ] + ], output, true + ) + else: + exit_code = OS.execute( + "bash", + [ + "-c", + "cd '%s' && zip -r '%s' ." % [ + source_dir, + ProjectSettings.globalize_path(target_zip_path) + ] + ], output, true + ) + + func _run_with_editor(item: Projects.Item, editor_flag: Callable, auto_close: bool) -> void: editor_flag.call(item) diff --git a/src/config.gd b/src/config.gd index eeb7c895..1e44d1be 100644 --- a/src/config.gd +++ b/src/config.gd @@ -15,6 +15,8 @@ const DEFAULT_VERSIONS_PATH = "user://versions" const DEFAULT_DOWNLOADS_PATH = "user://downloads" const DEFAULT_UPDATES_PATH = "user://updates" const DEFAULT_CACHE_DIR_PATH = "user://cache" +const TEMPLATES_CONFIG_PATH = "user://templates.cfg" +const DEFAULT_TEMPLATES_PATH = "user://templates" const RELEASES_URL = "https://github.com/MakovWait/godots/releases" const RELEASES_LATEST_API_ENDPOINT = "https://api.github.com/repos/MakovWait/godots/releases/latest" const RELEASES_API_ENDPOINT = "https://api.github.com/repos/MakovWait/godots/releases" diff --git a/src/main/gui/gui_main.gd b/src/main/gui/gui_main.gd index 053f6cfb..db934525 100644 --- a/src/main/gui/gui_main.gd +++ b/src/main/gui/gui_main.gd @@ -8,6 +8,7 @@ const theme_source = preload("res://theme/theme.gd") @export var _asset_lib_projects: AssetLibProjects @export var _godots_releases: GodotsReleasesControl @export var _rss_feed: RssFeedControl +@export var _project_templates: ProjectTemplatesControl @export var _auto_updates: AutoUpdates @export var _asset_download: PackedScene @export var _title_tabs: BoxContainer @@ -25,6 +26,7 @@ var _on_exit_tree_callbacks: Array[Callable] = [] var _local_remote_switch_context: LocalRemoteEditorsSwitchContext var _local_editors_service: LocalEditors.List var _projects_service: Projects.List +var _templates_service: ProjectTemplates.List func _ready() -> void: @@ -64,6 +66,7 @@ func _ready() -> void: _title_tabs.add_child(TitleTabButton.new("ProjectList", tr("Projects"), _tab_container, [_projects])) _title_tabs.add_child(TitleTabButton.new("AssetLib", tr("Asset Library"), _tab_container, [_asset_lib_projects])) _title_tabs.add_child(TitleTabButton.new("GodotMonochrome", tr("Editors"), _tab_container, [_local_editors, _remote_editors])) + _title_tabs.add_child(TitleTabButton.new("FileList", tr("Templates"), _tab_container, [_project_templates])) #_title_tabs.add_child(TitleTabButton.new("GodotMonochrome", tr("Remote Editors"), _tab_container, _remote_editors)) #_title_tabs.add_child(TitleTabButton.new(null, tr("Updates"), _tab_container, _updates)) _title_tabs.add_child(TitleTabButton.new("ExternalLink", tr("Feed"), _tab_container, [_rss_feed])) @@ -127,11 +130,13 @@ func _ready() -> void: _local_editors_service.load() _projects_service.load() + _templates_service.load() _projects.init(_projects_service) _local_editors.init(_local_editors_service) _remote_editors.init(%DownloadsContainer as DownloadsContainer) _rss_feed.init() + _project_templates.init(_templates_service, _projects_service) _projects.manage_tags_requested.connect(_popup_manage_tags) _local_editors.manage_tags_requested.connect(_popup_manage_tags) @@ -198,17 +203,24 @@ func _enter_tree() -> void: _local_editors_service, preload("res://assets/default_project_icon.svg") ) + _templates_service = ProjectTemplates.List.new( + Config.TEMPLATES_CONFIG_PATH, + Config.DEFAULT_TEMPLATES_PATH + ) Context.add(self, _local_remote_switch_context) Context.add(self, _local_editors_service) Context.add(self, _projects_service) + Context.add(self, _templates_service) _on_exit_tree_callbacks.append(func() -> void: _local_editors_service.cleanup() _projects_service.cleanup() + _templates_service.cleanup() Context.erase(self, _local_editors_service) Context.erase(self, _projects_service) + Context.erase(self, _templates_service) Context.erase(self, _local_remote_switch_context) ) diff --git a/src/main/gui/gui_main.tscn b/src/main/gui/gui_main.tscn index 8684d711..4953cd80 100644 --- a/src/main/gui/gui_main.tscn +++ b/src/main/gui/gui_main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3 uid="uid://omgg45dpxftx"] +[gd_scene load_steps=18 format=3 uid="uid://omgg45dpxftx"] [ext_resource type="Script" uid="uid://rj237qsrvde4" path="res://src/main/gui/gui_main.gd" id="1_yjveq"] [ext_resource type="PackedScene" uid="uid://cb6u1mub27xo" path="res://src/components/asset_download/asset_download.tscn" id="2_gfvot"] @@ -16,8 +16,9 @@ [ext_resource type="PackedScene" uid="uid://btxxnjmhy4bqw" path="res://src/components/command_viewer/command_viewer.tscn" id="13_mk5sf"] [ext_resource type="PackedScene" uid="uid://b3aprmu6od0wa" path="res://src/components/settings/settings_window.tscn" id="14_3j6hr"] [ext_resource type="PackedScene" path="res://src/components/rss_feed/rss_feed.tscn" id="15_rss"] +[ext_resource type="PackedScene" path="res://src/components/project_templates/project_templates.tscn" id="16_templates"] -[node name="Main" type="Control" node_paths=PackedStringArray("_remote_editors", "_local_editors", "_projects", "_asset_lib_projects", "_godots_releases", "_rss_feed", "_auto_updates", "_title_tabs", "_updates", "_tab_container")] +[node name="Main" type="Control" node_paths=PackedStringArray("_remote_editors", "_local_editors", "_projects", "_asset_lib_projects", "_godots_releases", "_rss_feed", "_project_templates", "_auto_updates", "_title_tabs", "_updates", "_tab_container")] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -31,6 +32,7 @@ _projects = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Local _asset_lib_projects = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Asset Library Projects") _godots_releases = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Updates") _rss_feed = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/RSS Feed") +_project_templates = NodePath("GuiBase/MainVBox/Content/VBoxContainer/TabContainer/Project Templates") _auto_updates = NodePath("AutoUpdates") _asset_download = ExtResource("2_gfvot") _title_tabs = NodePath("GuiBase/MainVBox/TitleBar/ButtonsContainer") @@ -107,6 +109,10 @@ layout_mode = 2 visible = false layout_mode = 2 +[node name="Project Templates" parent="GuiBase/MainVBox/Content/VBoxContainer/TabContainer" instance=ExtResource("16_templates")] +visible = false +layout_mode = 2 + [node name="DownloadsContainer" parent="GuiBase/MainVBox/Content/VBoxContainer" instance=ExtResource("9_drkdu")] unique_name_in_owner = true layout_mode = 2 diff --git a/src/services/project_templates.gd b/src/services/project_templates.gd new file mode 100644 index 00000000..c27c386c --- /dev/null +++ b/src/services/project_templates.gd @@ -0,0 +1,133 @@ +class_name ProjectTemplates + +const _dict = preload("res://src/extensions/dict.gd") + +class List extends RefCounted: + var _cfg := ConfigFile.new() + var _templates: Dictionary[String, Item] = {} + var _cfg_path: String + var _templates_dir: String + + func _init(cfg_path: String, templates_dir: String) -> void: + _cfg_path = cfg_path + _templates_dir = templates_dir + + func add(name: String, description: String, source_project_path: String, tags: Array = []) -> Item: + var id := _generate_id() + var section := "template_%s" % id + var template_zip_path := _templates_dir.path_join("%s.zip" % id) + + var item := Item.new( + section, + ConfigFileSection.new(section, IConfigFileLike.of_config(_cfg)), + template_zip_path + ) + item.name = name + item.description = description + item.source_project_path = source_project_path + item.tags = tags + item.created_at = int(Time.get_unix_time_from_system()) + _templates[id] = item + return item + + func all() -> Array[Item]: + var result: Array[Item] = [] + for x: Item in _templates.values(): + result.append(x) + return result + + func retrieve(id: String) -> Item: + return _templates.get(id, null) + + func has(id: String) -> bool: + return _templates.has(id) + + func erase(id: String) -> void: + var item := _templates.get(id, null) + if item: + # Delete the zip file + if FileAccess.file_exists(item.zip_path): + DirAccess.remove_absolute(item.zip_path) + _templates.erase(id) + _cfg.erase_section("template_%s" % id) + + # TODO type + func get_all_tags() -> Array: + var set := Set.new() + for template: Item in _templates.values(): + for tag: String in template.tags: + set.append(tag.to_lower()) + return set.values() + + func load() -> Error: + cleanup() + DirAccess.make_dir_recursive_absolute(_templates_dir) + var err := _cfg.load(_cfg_path) + if err and err != ERR_FILE_NOT_FOUND: + return err + for section in _cfg.get_sections(): + if section.begins_with("template_"): + var id := section.substr("template_".length()) + _templates[id] = Item.new( + section, + ConfigFileSection.new(section, IConfigFileLike.of_config(_cfg)), + _templates_dir.path_join("%s.zip" % id) + ) + return Error.OK + + func cleanup() -> void: + _dict.clear_and_free(_templates) + + func save() -> Error: + return _cfg.save(_cfg_path) + + func _generate_id() -> String: + return str(int(Time.get_unix_time_from_system() * 1000)) + "_" + str(randi() % 10000) + + +class Item: + signal internals_changed + signal loaded + + var id: String: + get: return _section.name.substr("template_".length()) + + var name: String: + get: return _section.get_value("name", "Unnamed Template") + set(value): _section.set_value("name", value) + + var description: String: + get: return _section.get_value("description", "") + set(value): _section.set_value("description", value) + + var source_project_path: String: + get: return _section.get_value("source_project_path", "") + set(value): _section.set_value("source_project_path", value) + + var zip_path: String: + get: return _zip_path + + var tags: Array: + get: return _section.get_value("tags", []) + set(value): _section.set_value("tags", value) + + var created_at: int: + get: return _section.get_value("created_at", 0) + set(value): _section.set_value("created_at", value) + + var is_zip_valid: bool: + get: return FileAccess.file_exists(_zip_path) + + var _section: ConfigFileSection + var _zip_path: String + + func _init(section_name: String, section: ConfigFileSection, zip_path: String) -> void: + _section = section + _zip_path = zip_path + + func get_formatted_date() -> String: + var datetime := Time.get_datetime_dict_from_unix_time(created_at) + return "%04d-%02d-%02d %02d:%02d" % [ + datetime.year, datetime.month, datetime.day, + datetime.hour, datetime.minute + ]