Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions src/components/editors/local/editor_item/editor_item.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class_name EditorListItemControl
extends HBoxListItem

signal edited
signal removed(remove_dir: bool)
signal removed(remove_dir: bool, remove_binary: bool)
signal manage_tags_requested
signal tag_clicked(tag: String)

Expand Down Expand Up @@ -299,23 +299,30 @@ func _on_remove(item: LocalEditors.Item) -> void:
warning.self_modulate = get_theme_color("warning_color", "Editor")
warning.hide()

var checkbox := CheckBox.new()
checkbox.text = tr("remove also from the file system")
checkbox.toggled.connect(func(toggled: bool) -> void:
var remove_binary_checkbox := CheckBox.new()
remove_binary_checkbox.text = tr("delete binary from filesystem")

var remove_dir_checkbox := CheckBox.new()
remove_dir_checkbox.text = tr("delete parent folder in filesystem")
remove_dir_checkbox.toggled.connect(func(toggled: bool) -> void:
warning.visible = toggled
if toggled:
remove_binary_checkbox.button_pressed = false
remove_binary_checkbox.disabled = toggled
)

var vb := VBoxContainer.new()
vb.add_child(label)
vb.add_child(checkbox)
vb.add_child(remove_binary_checkbox)
vb.add_child(remove_dir_checkbox)
vb.add_child(warning)
vb.add_spacer(false)

confirmation_dialog.add_child(vb)

confirmation_dialog.confirmed.connect(func() -> void:
queue_free()
removed.emit(checkbox.button_pressed)
removed.emit(remove_dir_checkbox.button_pressed, remove_binary_checkbox.button_pressed)
)
add_child(confirmation_dialog)
confirmation_dialog.popup_centered()
Expand Down
4 changes: 2 additions & 2 deletions src/components/editors/local/editors_list/editors_list.gd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class_name EditorsVBoxList
extends VBoxList

signal item_removed(item_data: LocalEditors.Item, remove_dir: bool)
signal item_removed(item_data: LocalEditors.Item, remove_dir: bool, remove_binary: bool)
signal item_edited(item_data: LocalEditors.Item)
signal item_manage_tags_requested(item_data: LocalEditors.Item)

Expand All @@ -10,7 +10,7 @@ func _post_add(raw_item_data: Object, raw_item_control: Control) -> void:
var item_data := raw_item_data as LocalEditors.Item
var item_control := raw_item_control as EditorListItemControl
item_control.removed.connect(
func(remove_dir: bool) -> void: item_removed.emit(item_data, remove_dir)
func(remove_dir: bool, remove_binary: bool) -> void: item_removed.emit(item_data, remove_dir, remove_binary)
)
item_control.edited.connect(
func() -> void: item_edited.emit(item_data)
Expand Down
36 changes: 27 additions & 9 deletions src/components/editors/local/local_editors.gd
Original file line number Diff line number Diff line change
Expand Up @@ -197,24 +197,42 @@ func _on_editors_list_item_selected(item: EditorListItemControl) -> void:
_sidebar.refresh_actions(item.get_actions())


func _on_editors_list_item_removed(item_data: LocalEditors.Item, remove_dir: bool) -> void:
func _on_editors_list_item_removed(item_data: LocalEditors.Item, remove_dir: bool, remove_binary: bool) -> void:
var target := _resolve_removal_target(
item_data.path, Config.VERSIONS_PATH.ret() as String, remove_dir, remove_binary
)
if remove_dir:
var base_dir := ProjectSettings.globalize_path(item_data.path.get_base_dir())
var versions_dir := ProjectSettings.globalize_path(Config.VERSIONS_PATH.ret() as String)
if not OS.has_feature("linux"):
base_dir = base_dir.to_lower()
versions_dir = versions_dir.to_lower()
if base_dir != versions_dir and base_dir.begins_with(versions_dir):
edir.remove_recursive(base_dir)
if target != "":
edir.remove_recursive(target)
else:
Output.push("skipping removing path {%s}" % base_dir)
Output.push("skipping removing path {%s}" % ProjectSettings.globalize_path(item_data.path.get_base_dir()))
elif remove_binary:
DirAccess.remove_absolute(target)
if _local_editors.has(item_data.path):
_local_editors.erase(item_data.path)
_local_editors.save()
_sidebar.refresh_actions([])
_update_remove_missing_disabled()


# Returns the absolute path to delete from the filesystem, or "" if nothing should be
# removed. The parent folder is only removable when it is a sub-folder of the versions
# dir (never the versions dir itself, e.g. for "extract to same folder" installs).
static func _resolve_removal_target(editor_path: String, versions_path: String, remove_dir: bool, remove_binary: bool) -> String:
if remove_dir:
var base_dir := ProjectSettings.globalize_path(editor_path.get_base_dir())
var versions_dir := ProjectSettings.globalize_path(versions_path)
if not OS.has_feature("linux"):
base_dir = base_dir.to_lower()
versions_dir = versions_dir.to_lower()
if base_dir != versions_dir and base_dir.begins_with(versions_dir):
return base_dir
return ""
if remove_binary:
return ProjectSettings.globalize_path(editor_path)
return ""


func _on_editors_list_item_edited(item_data: Variant) -> void:
_local_editors.save()
_editors_list.sort_items()
Expand Down
18 changes: 14 additions & 4 deletions src/components/editors/remote/remote_editors.gd
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,19 @@ func install_zip(zip_abs_path: String, root_unzip_folder_name: String, possible_


func _unzip_downloaded(downloaded_abs_path: String, root_unzip_folder_name: String) -> String:
var zip_content_dir := "%s/%s" % [Config.VERSIONS_PATH.ret(), root_unzip_folder_name]
if DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(zip_content_dir)):
zip_content_dir += "-%s" % uuid.v4().substr(0, 8)
zip_content_dir += "/"
var zip_content_dir := _resolve_unzip_dir(
Config.VERSIONS_PATH.ret() as String,
root_unzip_folder_name,
Config.EXTRACT_TO_SAME_FOLDER.ret() as bool
)
zip.unzip(downloaded_abs_path, zip_content_dir)
return zip_content_dir


static func _resolve_unzip_dir(versions_path: String, root_unzip_folder_name: String, extract_to_same_folder: bool) -> String:
if extract_to_same_folder:
return versions_path + "/"
var zip_content_dir := "%s/%s" % [versions_path, root_unzip_folder_name]
if DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(zip_content_dir)):
zip_content_dir += "-%s" % uuid.v4().substr(0, 8)
return zip_content_dir + "/"
6 changes: 6 additions & 0 deletions src/components/settings/settings_window.gd
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ func _prepare_settings() -> Array:
SettingCheckbox,
tr("Will check only stable Godots releases.")
)),
SettingChangeObserved(SettingCfg(
"application/advanced/extract_to_same_folder",
Config.EXTRACT_TO_SAME_FOLDER,
SettingCheckbox,
tr("Extract downloaded editors directly into the versions dir instead of creating a subfolder for each one.")
)),
SettingChangeObserved(SettingCfg(
"network/http_proxy/host",
Config.HTTP_PROXY_HOST,
Expand Down
15 changes: 12 additions & 3 deletions src/config.gd
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,20 @@ var ALLOW_INSTALL_TO_NOT_EMPTY_DIR := ConfigFileValue.new(


var ONLY_STABLE_UPDATES := ConfigFileValue.new(
_cfg_auto_save.as_config_like(),
"app",
_cfg_auto_save.as_config_like(),
"app",
"only_stable_updates",
true
):
):
set(_v): _readonly()


var EXTRACT_TO_SAME_FOLDER := ConfigFileValue.new(
_cfg_auto_save.as_config_like(),
"app",
"extract_to_same_folder",
false
):
set(_v): _readonly()


Expand Down
32 changes: 32 additions & 0 deletions tests/cases/editors/resolve_removal_target_test.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
extends GdUnitTestSuite


func test_delete_parent_folder_returns_the_editor_subfolder() -> void:
assert_str(
LocalEditorsControl._resolve_removal_target("/versions/Godot_v4.5-stable/godot", "/versions", true, false)
).is_equal("/versions/Godot_v4.5-stable")


func test_delete_parent_folder_is_skipped_for_same_folder_installs() -> void:
# The binary lives directly in the versions dir, so the guard refuses to remove it.
assert_str(
LocalEditorsControl._resolve_removal_target("/versions/godot", "/versions", true, false)
).is_equal("")


func test_delete_parent_folder_is_skipped_outside_the_versions_dir() -> void:
assert_str(
LocalEditorsControl._resolve_removal_target("/elsewhere/godot", "/versions", true, false)
).is_equal("")


func test_delete_binary_returns_the_binary_path() -> void:
assert_str(
LocalEditorsControl._resolve_removal_target("/versions/godot", "/versions", false, true)
).is_equal("/versions/godot")


func test_no_option_selected_removes_nothing() -> void:
assert_str(
LocalEditorsControl._resolve_removal_target("/versions/Godot_v4.5-stable/godot", "/versions", false, false)
).is_equal("")
1 change: 1 addition & 0 deletions tests/cases/editors/resolve_removal_target_test.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://bbkjp7dksyl5s
13 changes: 13 additions & 0 deletions tests/cases/editors/resolve_unzip_dir_test.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extends GdUnitTestSuite


func test_extract_to_same_folder_uses_versions_path_directly() -> void:
assert_str(
RemoteEditorsControl._resolve_unzip_dir("user://__test_versions__", "Godot_v4.5-stable", true)
).is_equal("user://__test_versions__/")


func test_extract_creates_subfolder_named_after_the_release() -> void:
assert_str(
RemoteEditorsControl._resolve_unzip_dir("user://__test_versions__", "Godot_v4.5-stable", false)
).is_equal("user://__test_versions__/Godot_v4.5-stable/")
1 change: 1 addition & 0 deletions tests/cases/editors/resolve_unzip_dir_test.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://d2cqe4bdjy68i