Sets up initial project structure
Initializes the project with core files including: - Editor configuration (.editorconfig, .gitattributes, .gitignore, .vscode/settings.json) - Log.gd addon for enhanced debugging - Loggie addon for advanced logging - Project assets folder
This commit is contained in:
parent
f8140c83ff
commit
02b3be35b0
144 changed files with 7399 additions and 0 deletions
335
addons/loggie/version_management/loggie_update.gd
Normal file
335
addons/loggie/version_management/loggie_update.gd
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
@tool
|
||||
class_name LoggieUpdate extends Node
|
||||
|
||||
## Emitted when this update fails.
|
||||
signal failed()
|
||||
|
||||
## Emitted when this update succeeds.
|
||||
signal succeeded()
|
||||
|
||||
## Emitted when this declares that it has made some progress.
|
||||
signal progress(value : float)
|
||||
|
||||
## Emitted when this declares that it wants a new status/substatus message to be used.
|
||||
signal status_changed(status_msg : Variant, substatus_msg : Variant)
|
||||
|
||||
## Emitted when this update is starting.
|
||||
signal starting()
|
||||
|
||||
## Emitted when the 'is_in_progress' status of this update changes.
|
||||
signal is_in_progress_changed(new_value : bool)
|
||||
|
||||
## The path to the directory that should have a temporary file created and filled with the patch zipball buffer.
|
||||
const TEMP_FILES_DIR = "user://"
|
||||
|
||||
## If this is set to a non-empty string, it will be used as the directory into which the new update will be
|
||||
## installed. Used for testing/debugging. When set to empty string, Loggie will automatically figure out
|
||||
## where it is being updated from and use that directory instead.
|
||||
const ALT_LOGGIE_PLUGIN_CONTAINER_DIR = ""
|
||||
|
||||
## The domain from which status report [LoggieMsg]s from this update will be logged from.
|
||||
const REPORTS_DOMAIN : String = "loggie_update_status_reports"
|
||||
|
||||
## Stores a reference to the logger that's requesting this update.
|
||||
var _logger : Variant
|
||||
|
||||
## The URL used to visit a page that contains the release notes for this update.
|
||||
var release_notes_url = ""
|
||||
|
||||
## Stores a reference to the previous version the connected [member _logger] is/was using.
|
||||
var prev_version : LoggieVersion = null
|
||||
|
||||
## Stores a reference to the new version the connected [member _logger] should be using after the update.
|
||||
var new_version : LoggieVersion = null
|
||||
|
||||
## Indicates whether this update is currently in progress.
|
||||
var is_in_progress : bool = false
|
||||
|
||||
## Whether the update should retain or purge the backup it makes of the previous version files once it is done
|
||||
## installing and applying the new update.
|
||||
var _clean_up_backup_files : bool = true
|
||||
|
||||
func _init(_prev_version : LoggieVersion, _new_version : LoggieVersion) -> void:
|
||||
self.prev_version = _prev_version
|
||||
self.new_version = _new_version
|
||||
|
||||
## Returns a reference to the logger that's requesting this update.
|
||||
func get_logger() -> Variant:
|
||||
return self._logger
|
||||
|
||||
## Sets the URL used to visit a page that contains the release notes for this update.
|
||||
func set_release_notes_url(url : String) -> void:
|
||||
self.release_notes_url = url
|
||||
|
||||
## Sets whether this window is currently performing the update.
|
||||
func set_is_in_progress(value : bool) -> void:
|
||||
self.is_in_progress = value
|
||||
self.is_in_progress_changed.emit(value)
|
||||
|
||||
## Tries to start the version update. Prevents the update from starting
|
||||
## if something is not configured correctly and pushes a warning/error.
|
||||
func try_start():
|
||||
if Engine.has_meta("LoggieUpdateSuccessful") and Engine.get_meta("LoggieUpdateSuccessful"):
|
||||
# No plan to allow multiple updates to run during a single Engine session anyway so no need to start another one.
|
||||
# Also, this helps with internal testing of the updater and prevents an updated plugin from auto-starting another update
|
||||
# when dealing with proxy versions.
|
||||
return
|
||||
|
||||
if self._logger == null:
|
||||
push_warning("Attempt to start Loggie update failed - member '_logger' on the LoggieUpdate object is null.")
|
||||
return
|
||||
|
||||
if self.is_in_progress:
|
||||
push_warning("Attempt to start Loggie update failed - the update is already in progress.")
|
||||
return
|
||||
|
||||
if self.new_version == null or self.prev_version == null:
|
||||
push_warning("Attempt to start Loggie update failed - the updater prompt has the 'new_version' or 'prev_version' variable at null value.")
|
||||
return
|
||||
elif !self.new_version.is_higher_than(self.prev_version):
|
||||
push_warning("Attempt to start Loggie update failed - the 'new_version' is not higher than 'prev_version'.")
|
||||
return
|
||||
|
||||
if self.new_version.has_meta("github_data"):
|
||||
var github_data = self.new_version.get_meta("github_data")
|
||||
if !github_data.has("zipball_url"):
|
||||
push_error("Attempt to start Loggie update failed - the meta key 'github_data' on the 'new_version' is a dictionary that does not contain the required 'zipball_url' key/value pair.")
|
||||
return
|
||||
else:
|
||||
push_error("Attempt to start Loggie update failed - the meta key 'github_data' on the 'new_version' was not found.")
|
||||
return
|
||||
|
||||
_start()
|
||||
|
||||
## Internal function. Starts the updating of the [param _logger] to the [param new_version].
|
||||
## Do not run without verification that configuration is correct.
|
||||
## Use [method try_start] to call this safely.
|
||||
func _start():
|
||||
var loggie = self.get_logger()
|
||||
|
||||
loggie.msg("Loggie is updating from version {v_prev} to {v_new}.".format({
|
||||
"v_prev" : self.prev_version,
|
||||
"v_new" : self.new_version
|
||||
})).domain(REPORTS_DOMAIN).color(Color.ORANGE).box(12).info()
|
||||
|
||||
set_is_in_progress(true)
|
||||
starting.emit()
|
||||
|
||||
# Make request to configured endpoint.
|
||||
var update_data = self.new_version.get_meta("github_data")
|
||||
var http_request = HTTPRequest.new()
|
||||
loggie.add_child(http_request)
|
||||
http_request.request_completed.connect(_on_download_request_completed)
|
||||
http_request.request(update_data.zipball_url)
|
||||
|
||||
## Internal callback function.
|
||||
## Defines what happens when new update content is successfully downloaded from GitHub.
|
||||
## Called automatically during [method _start] if everything is going according to plan.
|
||||
func _on_download_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
var loggie = self.get_logger()
|
||||
|
||||
if loggie == null:
|
||||
_failure("The _logger used by the updater window is null.")
|
||||
return
|
||||
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
_failure("Download request returned non-zero code: " + str(result))
|
||||
return
|
||||
|
||||
#region || Prepare: Define variables and callbacks that will be used throughout.
|
||||
# The path to the directory which is supposed to contain the plugin directory.
|
||||
# This will usually be 'res://addons/', but could be anything else too. We'll read it dynamically
|
||||
# from the connected logger to guarantee correctness.
|
||||
var LOGGIE_PLUGIN_CONTAINER_DIR = ALT_LOGGIE_PLUGIN_CONTAINER_DIR if !ALT_LOGGIE_PLUGIN_CONTAINER_DIR.is_empty() else loggie.get_directory_path().get_base_dir() + "/"
|
||||
|
||||
# The path to the `loggie` plugin directory.
|
||||
var LOGGIE_PLUGIN_DIR = ProjectSettings.globalize_path(LOGGIE_PLUGIN_CONTAINER_DIR.path_join("loggie/"))
|
||||
|
||||
# The full path filename of the temporary .zip archive that will be created to store the downloaded data.
|
||||
var TEMP_ZIP_FILE_PATH = ProjectSettings.globalize_path(TEMP_FILES_DIR.path_join("_temp_loggie_{ver}.zip".format({"ver": str(new_version)})))
|
||||
|
||||
# The path to the directory where a temporary backup of current loggie plugin files will be copied to.
|
||||
# (will be created if doesn't exist).
|
||||
var TEMP_PREV_VER_FILES_DIR_PATH = ProjectSettings.globalize_path(TEMP_FILES_DIR.path_join("_temp_loggie_{ver}_backup".format({"ver": str(prev_version)})))
|
||||
|
||||
# A callable that can be reused within this function that cleans up the temporary and unused directories,
|
||||
# once this function comes to a conclusion.
|
||||
var clean_up : Callable = func():
|
||||
if FileAccess.file_exists(TEMP_ZIP_FILE_PATH):
|
||||
OS.move_to_trash(TEMP_ZIP_FILE_PATH)
|
||||
if DirAccess.dir_exists_absolute(TEMP_PREV_VER_FILES_DIR_PATH) and self._clean_up_backup_files:
|
||||
OS.move_to_trash(TEMP_PREV_VER_FILES_DIR_PATH)
|
||||
|
||||
# A callable that can be used to replace the currently existing Loggie plugin directory
|
||||
# with whatever is currently (temporarily) stored as its backup.
|
||||
var revert_to_backup = func():
|
||||
if FileAccess.file_exists(LOGGIE_PLUGIN_DIR):
|
||||
OS.move_to_trash(LOGGIE_PLUGIN_DIR)
|
||||
if DirAccess.dir_exists_absolute(TEMP_PREV_VER_FILES_DIR_PATH):
|
||||
DirAccess.rename_absolute(TEMP_PREV_VER_FILES_DIR_PATH, LOGGIE_PLUGIN_DIR)
|
||||
|
||||
#endregion
|
||||
|
||||
#region || Step 1: Store the downloaded content into a temporary zip file.
|
||||
send_progress_update(20, "Processing Files", "Storing patch locally...")
|
||||
|
||||
var zip_file: FileAccess = FileAccess.open(TEMP_ZIP_FILE_PATH, FileAccess.WRITE)
|
||||
if zip_file == null:
|
||||
_failure("Failed to open temp. file for writing: {path}".format({"path": TEMP_ZIP_FILE_PATH}))
|
||||
clean_up.call()
|
||||
return
|
||||
|
||||
zip_file.store_buffer(body)
|
||||
zip_file.close()
|
||||
#endregion
|
||||
|
||||
#region || Step 2: Make a temporary backup of the currently used Loggie plugin directory.
|
||||
send_progress_update(30, "Processing Files", "Backing up previous version files...")
|
||||
|
||||
if !DirAccess.dir_exists_absolute(LOGGIE_PLUGIN_DIR):
|
||||
_failure("The Loggie plugin directory ({path}) could not be found.".format({
|
||||
"path" : LOGGIE_PLUGIN_DIR
|
||||
}))
|
||||
clean_up.call()
|
||||
return
|
||||
|
||||
var copy_prev_ver_result = LoggieTools.copy_dir_absolute(LOGGIE_PLUGIN_DIR, TEMP_PREV_VER_FILES_DIR_PATH, true)
|
||||
if copy_prev_ver_result.errors.size() > 0:
|
||||
var copy_prev_var_result_errors_msg = LoggieMsg.new("Errors encountered:")
|
||||
for error in copy_prev_ver_result.errors:
|
||||
copy_prev_var_result_errors_msg.nl().add(error_string(error))
|
||||
_failure(copy_prev_var_result_errors_msg.string())
|
||||
clean_up.call()
|
||||
return
|
||||
#endregion
|
||||
|
||||
#region || Step 3: Remove currently used Loggie plugin directory and create a new one in its place populated with new version files.
|
||||
send_progress_update(50, "Processing Files", "Copying new version files...")
|
||||
var zip_reader: ZIPReader = ZIPReader.new()
|
||||
var zip_reader_open_error = zip_reader.open(TEMP_ZIP_FILE_PATH)
|
||||
if zip_reader_open_error != OK:
|
||||
_failure("Attempt to open temp. file(s) archive at {path} failed with error: {err_str}".format({
|
||||
"path": LOGGIE_PLUGIN_DIR,
|
||||
"err_str" : error_string(zip_reader_open_error)
|
||||
}))
|
||||
clean_up.call()
|
||||
return
|
||||
|
||||
# Trash the previously existing loggie plugin dir entirely.
|
||||
# A new one will be created in a moment.
|
||||
OS.move_to_trash(LOGGIE_PLUGIN_DIR)
|
||||
|
||||
# Get a list of all files and dirs in the zip.
|
||||
var files : PackedStringArray = zip_reader.get_files()
|
||||
|
||||
# This will always be the "addons" directory in the zip archive in which we expect
|
||||
# to find the "loggie" directory containing the plugin.
|
||||
var base_path_in_zip = files[1]
|
||||
|
||||
# Remove the first 2 parts of the path that we won't be needing at all.
|
||||
files.remove_at(0)
|
||||
files.remove_at(0)
|
||||
|
||||
# Create all needed files and directories.
|
||||
for path in files:
|
||||
var new_file_path: String = path.replace(base_path_in_zip, "")
|
||||
if path.ends_with("/"):
|
||||
DirAccess.make_dir_recursive_absolute(LOGGIE_PLUGIN_CONTAINER_DIR + new_file_path)
|
||||
else:
|
||||
var abs_path = LOGGIE_PLUGIN_CONTAINER_DIR + new_file_path
|
||||
var file : FileAccess = FileAccess.open(abs_path, FileAccess.WRITE)
|
||||
if file == null:
|
||||
_failure("Error while storing buffer data into temporary files - write target directory or file {target} gave the error: {error}".format({
|
||||
"error" : error_string(FileAccess.get_open_error()),
|
||||
"target" : abs_path
|
||||
}))
|
||||
revert_to_backup.call()
|
||||
clean_up.call()
|
||||
return
|
||||
else:
|
||||
var file_content = zip_reader.read_file(path)
|
||||
file.store_buffer(file_content)
|
||||
file.close()
|
||||
|
||||
zip_reader.close()
|
||||
#endregion
|
||||
|
||||
#region || Step 4: Move the user's 'custom_settings.gd' to the new version directory if it existed in prev version.
|
||||
send_progress_update(70, "Processing Files", "Reapplying custom settings...")
|
||||
var CUSTOM_SETTINGS_IN_PREV_VER_PATH = TEMP_PREV_VER_FILES_DIR_PATH.path_join("custom_settings.gd")
|
||||
if FileAccess.file_exists(CUSTOM_SETTINGS_IN_PREV_VER_PATH):
|
||||
var CUSTOM_SETTINGS_IN_NEW_VER_PATH = ProjectSettings.globalize_path(LOGGIE_PLUGIN_DIR.path_join("custom_settings.gd"))
|
||||
var custom_settings_copy_error = DirAccess.copy_absolute(CUSTOM_SETTINGS_IN_PREV_VER_PATH, CUSTOM_SETTINGS_IN_NEW_VER_PATH)
|
||||
if custom_settings_copy_error != OK:
|
||||
push_error("Attempt to copy the 'custom_settings.gd' file from {p1} to {p2} failed with error: {error}".format({
|
||||
"p1" : CUSTOM_SETTINGS_IN_PREV_VER_PATH,
|
||||
"p2" : CUSTOM_SETTINGS_IN_NEW_VER_PATH,
|
||||
"error" : error_string(custom_settings_copy_error)
|
||||
}))
|
||||
#endregion
|
||||
|
||||
#region || Step 5: Move the user's 'channels/custom_channels' directory to the new version if it existed in prev version.
|
||||
send_progress_update(80, "Processing Files", "Reapplying custom channels...")
|
||||
var CUSTOM_CHANNELS_IN_PREV_VER_PATH = ProjectSettings.globalize_path(TEMP_PREV_VER_FILES_DIR_PATH.path_join("channels/custom_channels/"))
|
||||
if DirAccess.dir_exists_absolute(CUSTOM_CHANNELS_IN_PREV_VER_PATH):
|
||||
var CUSTOM_CHANNELS_IN_NEW_VER_PATH = ProjectSettings.globalize_path(LOGGIE_PLUGIN_DIR.path_join("channels/custom_channels/"))
|
||||
var copy_prev_ver_custom_channels_result = LoggieTools.copy_dir_absolute(CUSTOM_CHANNELS_IN_PREV_VER_PATH, CUSTOM_CHANNELS_IN_NEW_VER_PATH, true)
|
||||
if copy_prev_ver_custom_channels_result.errors.size() > 0:
|
||||
var copy_prev_var_result_errors_msg = LoggieMsg.new("Errors encountered:")
|
||||
for error in copy_prev_ver_result.errors:
|
||||
copy_prev_var_result_errors_msg.nl().add(error_string(error))
|
||||
push_error("Attempt to copy the 'channels/custom_channels' directory from {p1} to {p2} failed with error: {error}".format({
|
||||
"p1" : CUSTOM_CHANNELS_IN_PREV_VER_PATH,
|
||||
"p2" : CUSTOM_CHANNELS_IN_NEW_VER_PATH,
|
||||
"error" : copy_prev_var_result_errors_msg.string()
|
||||
}))
|
||||
else:
|
||||
print("The {path} directory doesn't exist.".format({"path": CUSTOM_CHANNELS_IN_PREV_VER_PATH}))
|
||||
#endregion
|
||||
|
||||
#region || Step 6: Clean up temporarily created files and close filewrite.
|
||||
send_progress_update(90, "Processing Files", "Cleaning up...")
|
||||
clean_up.call()
|
||||
#endregion
|
||||
|
||||
#region || Step 7: Declare successful. Wrap up.
|
||||
send_progress_update(100, "Finishing up", "")
|
||||
_success()
|
||||
#endregion
|
||||
|
||||
## Internal function used at the end of the updating process if it is successfully completed.
|
||||
func _success():
|
||||
set_is_in_progress(false)
|
||||
|
||||
var msg = "💬 You may see temporary errors in the console due to Loggie files being re-scanned and reloaded on the spot.\nIt should be safe to dismiss them, but for the best experience, reload the Godot editor (and the plugin, if something seems wrong).\n\n🚩 If you see a 'Files have been modified on disk' window pop up, choose 'Discard local changes and reload' to accept incoming changes."
|
||||
status_changed.emit(null, msg)
|
||||
succeeded.emit()
|
||||
|
||||
print_rich(LoggieMsg.new("👀 Loggie updated to version {new_ver}!".format({"new_ver": self.new_version})).bold().color(Color.ORANGE).string())
|
||||
print_rich(LoggieMsg.new("\t📚 Release Notes: ").bold().msg("[url={url}]{url}[/url]".format({"url": release_notes_url})).color(Color.CORNFLOWER_BLUE).string())
|
||||
print_rich(LoggieMsg.new("\t💬 Support, Development & Feature Requests: ").bold().msg("[url=https://discord.gg/XPdxpMqmcs]https://discord.gg/XPdxpMqmcs[/url]").color(Color.CORNFLOWER_BLUE).string())
|
||||
|
||||
if Engine.is_editor_hint():
|
||||
var editor_plugin = Engine.get_meta("LoggieEditorPlugin")
|
||||
editor_plugin.get_editor_interface().get_resource_filesystem().scan()
|
||||
editor_plugin.get_editor_interface().call_deferred("set_plugin_enabled", "loggie", true)
|
||||
editor_plugin.get_editor_interface().set_plugin_enabled("loggie", false)
|
||||
Engine.set_meta("LoggieUpdateSuccessful", true)
|
||||
print_rich("[b]Updater:[/b] ", msg)
|
||||
|
||||
## Internal function used to interrupt an ongoing update and cause it to fail.
|
||||
func _failure(status_msg : String):
|
||||
var loggie = self.get_logger()
|
||||
loggie.msg(status_msg).color(Color.SALMON).preprocessed(false).error()
|
||||
loggie.msg("\t💬 If this issue persists, consider reporting: ").bold().msg("https://github.com/Shiva-Shadowsong/loggie/issues").color(Color.CORNFLOWER_BLUE).preprocessed(false).info()
|
||||
set_is_in_progress(false)
|
||||
failed.emit()
|
||||
status_changed.emit(null, status_msg)
|
||||
|
||||
## Informs the listeners of the [signal progress] / [signal status_changed] signals about a change in the progress of the update.
|
||||
func send_progress_update(progress_amount : float, status_msg : String, substatus_msg : String):
|
||||
var loggie = self.get_logger()
|
||||
if !substatus_msg.is_empty():
|
||||
loggie.msg("•• ").msg(substatus_msg).domain(REPORTS_DOMAIN).preprocessed(false).info()
|
||||
progress.emit(progress_amount)
|
||||
status_changed.emit(status_msg, substatus_msg)
|
||||
1
addons/loggie/version_management/loggie_update.gd.uid
Normal file
1
addons/loggie/version_management/loggie_update.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://dr553ffcn7yyg
|
||||
39
addons/loggie/version_management/loggie_version.gd
Normal file
39
addons/loggie/version_management/loggie_version.gd
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
## A utility class that helps with storing data about a Loggie Version and converting and comparing version strings.
|
||||
class_name LoggieVersion extends Resource
|
||||
|
||||
var minor : int = -1 ## The minor component of the version.
|
||||
var major : int = -1 ## The major component of the version.
|
||||
var proxy_for : LoggieVersion = null ## The version that this version is a proxy for. (Internal use only)
|
||||
|
||||
func _init(_major : int = -1, _minor : int = -1) -> void:
|
||||
self.minor = _minor
|
||||
self.major = _major
|
||||
|
||||
func _to_string() -> String:
|
||||
return str(self.major) + "." + str(self.minor)
|
||||
|
||||
## Checks if this version is valid.
|
||||
## (neither minor nor major component can be less than 0).
|
||||
func is_valid() -> bool:
|
||||
return (minor >= 0 and major >= 0)
|
||||
|
||||
## Checks if the given [param version] if higher than this version.
|
||||
func is_higher_than(version : LoggieVersion):
|
||||
if self.major > version.major:
|
||||
return true
|
||||
if self.minor > version.minor:
|
||||
return true
|
||||
return false
|
||||
|
||||
## Given a string that has 2 sets of digits separated by a ".", breaks that down
|
||||
## into a version with a major and minor version component (ints).
|
||||
static func from_string(version_string : String) -> LoggieVersion:
|
||||
var version : LoggieVersion = LoggieVersion.new()
|
||||
var regex = RegEx.new()
|
||||
regex.compile("(\\d+)\\.(\\d+)")
|
||||
|
||||
var result = regex.search(version_string)
|
||||
if result:
|
||||
version.major = result.get_string(1).to_int()
|
||||
version.minor = result.get_string(2).to_int()
|
||||
return version
|
||||
1
addons/loggie/version_management/loggie_version.gd.uid
Normal file
1
addons/loggie/version_management/loggie_version.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bxwv6sw76aqxm
|
||||
225
addons/loggie/version_management/loggie_version_manager.gd
Normal file
225
addons/loggie/version_management/loggie_version_manager.gd
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
@tool
|
||||
## A class that can be used to inquire about, generate, and operate on [LoggieVersion]s.
|
||||
## It is also responsible for notifying about an available update, and starting it, if configured to do so.
|
||||
class_name LoggieVersionManager extends RefCounted
|
||||
|
||||
## Emitted when this version manager updates the known [member latest_version].
|
||||
signal latest_version_updated()
|
||||
|
||||
## Emitted when this version manager has created a valid [LoggieUpdate] and is ready to use it.
|
||||
signal update_ready()
|
||||
|
||||
## The URL where loggie releases on GitHub can be found.
|
||||
const REMOTE_RELEASES_URL = "https://api.github.com/repos/Shiva-Shadowsong/loggie/releases"
|
||||
|
||||
## The domain from which [LoggieMsg]s from this version manager will be logged from.
|
||||
const REPORTS_DOMAIN : String = "loggie_version_check_reports"
|
||||
|
||||
## Stores the result of reading the Loggie version with [method get_current_Version].
|
||||
var version : LoggieVersion = null
|
||||
|
||||
## Stores the result of reading the latest Loggie version with [method get_latest_version].
|
||||
var latest_version : LoggieVersion = null
|
||||
|
||||
## Stores a reference to a ConfigFile which will be loaded from [member CONFIG_PATH] during [method find_and_store_current_version].
|
||||
var config : ConfigFile = ConfigFile.new()
|
||||
|
||||
## Stores a reference to the logger that's using this version manager.
|
||||
var _logger : Variant = null
|
||||
|
||||
## Stores a reference to the [LoggieUpdate] that has been created to handle an available update.
|
||||
var _update : LoggieUpdate = null
|
||||
|
||||
## Internal debug variable.
|
||||
## If not null, this version manager will treat the [LoggieVersion] provided under this variable to be the current [param version].
|
||||
## Useful for debugging this module when you want to simulate that the current version is something different than it actually is.
|
||||
var _version_proxy : LoggieVersion = null
|
||||
|
||||
## Initializes this version manager, connecting it to the logger that's using it and updating the version cache based on that logger,
|
||||
## which will further prompt the emission of signals in this class, and so on.
|
||||
func connect_logger(logger : Variant) -> void:
|
||||
self.latest_version_updated.connect(on_latest_version_updated)
|
||||
self._logger = logger
|
||||
|
||||
# Set to true during development to enable debug prints related to version management.
|
||||
self._logger.set_domain_enabled(self.REPORTS_DOMAIN, false)
|
||||
|
||||
update_version_cache()
|
||||
|
||||
## Returns a reference to the logger object that is using this version manager.
|
||||
func get_logger() -> Variant:
|
||||
return self._logger
|
||||
|
||||
## Reads the current version of Loggie from plugin.cfg and stores it in [member version].
|
||||
func find_and_store_current_version():
|
||||
var detected_version = self._logger.version
|
||||
if self._version_proxy != null:
|
||||
self.version = self._version_proxy
|
||||
self.version.proxy_for = detected_version
|
||||
else:
|
||||
self.version = detected_version
|
||||
|
||||
## Reads the latest version of Loggie from a GitHub API response and stores it in [member latest_version].
|
||||
func find_and_store_latest_version():
|
||||
var loggie = self.get_logger()
|
||||
var http_request = HTTPRequest.new()
|
||||
loggie.add_child(http_request)
|
||||
loggie.msg("Retrieving version(s) info from endpoint:", REMOTE_RELEASES_URL).domain(REPORTS_DOMAIN).debug()
|
||||
http_request.request_completed.connect(_on_get_latest_version_request_completed, CONNECT_ONE_SHOT)
|
||||
http_request.request(REMOTE_RELEASES_URL)
|
||||
|
||||
## Defines what happens once this version manager emits the signal saying that an update is available.
|
||||
func on_update_available_detected() -> void:
|
||||
var loggie = self.get_logger()
|
||||
if loggie.settings.update_check_mode == LoggieEnums.UpdateCheckType.DONT_CHECK:
|
||||
return
|
||||
|
||||
self._update = LoggieUpdate.new(self.version, self.latest_version)
|
||||
self._update._logger = loggie
|
||||
|
||||
var github_data = self.latest_version.get_meta("github_data")
|
||||
var latest_release_notes_url = github_data.html_url
|
||||
self._update.set_release_notes_url(latest_release_notes_url)
|
||||
loggie.add_child(self._update)
|
||||
update_ready.emit()
|
||||
|
||||
# No plan to allow multiple updates to run during a single Engine session anyway so no need to start another one.
|
||||
# Also, this helps with internal testing of the updater and prevents an updated plugin from auto-starting another update
|
||||
# when dealing with proxy versions.
|
||||
var hasUpdatedAlready = Engine.has_meta("LoggieUpdateSuccessful") and Engine.get_meta("LoggieUpdateSuccessful")
|
||||
|
||||
match loggie.settings.update_check_mode:
|
||||
LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_UPDATER_WINDOW:
|
||||
if hasUpdatedAlready:
|
||||
loggie.info("Update already performed. ✔️")
|
||||
return
|
||||
create_and_show_updater_widget(self._update)
|
||||
LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_MSG:
|
||||
loggie.msg("👀 Loggie update available!").color(Color.ORANGE).header().msg(" > Current version: {version}, Latest version: {latest}".format({
|
||||
"version" : self.version,
|
||||
"latest" : self.latest_version
|
||||
})).info()
|
||||
LoggieEnums.UpdateCheckType.CHECK_DOWNLOAD_AND_SHOW_MSG:
|
||||
if hasUpdatedAlready:
|
||||
loggie.info("Update already performed. ✔️")
|
||||
return
|
||||
loggie.set_domain_enabled("loggie_update_status_reports", true)
|
||||
self._update.try_start()
|
||||
|
||||
## Defines what happens when the request to GitHub API which grabs all the Loggie releases is completed.
|
||||
func _on_get_latest_version_request_completed(result : int, response_code : int, headers : PackedStringArray, body: PackedByteArray):
|
||||
var loggie = self.get_logger()
|
||||
loggie.msg("Response for request received:", response_code).domain(REPORTS_DOMAIN).debug()
|
||||
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
return
|
||||
|
||||
var response = JSON.parse_string(body.get_string_from_utf8())
|
||||
|
||||
if typeof(response) != TYPE_ARRAY:
|
||||
loggie.error("The response parsed from GitHub was not an array. Response received in an unsupported format.")
|
||||
return
|
||||
|
||||
var latest_version_data = response[0] # GitHub releases are in order of creation, so grab the first one from the response, that's the latest one.
|
||||
self.latest_version = LoggieVersion.from_string(latest_version_data.tag_name)
|
||||
self.latest_version.set_meta("github_data", latest_version_data)
|
||||
|
||||
loggie.msg("Current version of Loggie:", self.version).msg(" (proxy for: {value})".format({"value": self.version.proxy_for})).domain(REPORTS_DOMAIN).debug()
|
||||
loggie.msg("Latest version of Loggie:", self.latest_version).domain(REPORTS_DOMAIN).debug()
|
||||
latest_version_updated.emit()
|
||||
|
||||
## Executes every time this version manager updates the known latest_version.
|
||||
func on_latest_version_updated() -> void:
|
||||
var loggie = self.get_logger()
|
||||
if loggie == null:
|
||||
return
|
||||
|
||||
# Check if update is available.
|
||||
if loggie.settings.update_check_mode != LoggieEnums.UpdateCheckType.DONT_CHECK:
|
||||
loggie.msg("👀 Loggie:").bold().color("orange").msg(" Checking for updates...").info()
|
||||
if is_update_available():
|
||||
on_update_available_detected()
|
||||
else:
|
||||
loggie.msg("👀 Loggie:").bold().color("orange").msg(" Up to date. ✔️").color(Color.LIGHT_GREEN).info()
|
||||
|
||||
## Displays the widget which informs the user of the available update and offers actions that they can take next.
|
||||
func create_and_show_updater_widget(update : LoggieUpdate) -> Window:
|
||||
const PATH_TO_WIDGET_SCENE = "addons/loggie/version_management/update_prompt_window.tscn"
|
||||
var WIDGET_SCENE = load(PATH_TO_WIDGET_SCENE)
|
||||
if !is_instance_valid(WIDGET_SCENE):
|
||||
push_error("Loggie Update Prompt Window scene not found on expected path: {path}".format({"path": PATH_TO_WIDGET_SCENE}))
|
||||
return
|
||||
|
||||
var loggie = self.get_logger()
|
||||
if loggie == null:
|
||||
return
|
||||
|
||||
var popup_parent = null
|
||||
if Engine.is_editor_hint() and Engine.has_meta("LoggieEditorInterfaceBaseControl"):
|
||||
popup_parent = Engine.get_meta("LoggieEditorInterfaceBaseControl")
|
||||
else:
|
||||
popup_parent = SceneTree.current_scene
|
||||
|
||||
# Configure popup window.
|
||||
var _popup = Window.new()
|
||||
update.succeeded.connect(func():
|
||||
_popup.queue_free()
|
||||
var success_dialog = AcceptDialog.new()
|
||||
var msg = "💬 You may see temporary errors in the console due to Loggie files being re-scanned and reloaded on the spot.\nIt should be safe to dismiss them, but for the best experience, reload the Godot editor (and the plugin, if something seems wrong).\n\n🚩 If you see a 'Files have been modified on disk' window pop up, choose 'Discard local changes and reload' to accept incoming changes."
|
||||
success_dialog.dialog_text = msg
|
||||
success_dialog.title = "Loggie Updater"
|
||||
if is_instance_valid(popup_parent):
|
||||
popup_parent.add_child(success_dialog)
|
||||
success_dialog.popup_centered()
|
||||
)
|
||||
var on_close_requested = func():
|
||||
_popup.queue_free()
|
||||
|
||||
_popup.close_requested.connect(on_close_requested, CONNECT_ONE_SHOT)
|
||||
_popup.borderless = false
|
||||
_popup.unresizable = true
|
||||
_popup.transient = true
|
||||
_popup.title = "Update Available"
|
||||
|
||||
# Configure window widget and add it as a child of the popup window.
|
||||
var widget : LoggieUpdatePrompt = WIDGET_SCENE.instantiate()
|
||||
widget.connect_to_update(update)
|
||||
widget.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
widget._logger = loggie
|
||||
widget.close_requested.connect(on_close_requested, CONNECT_ONE_SHOT)
|
||||
|
||||
if is_instance_valid(popup_parent):
|
||||
popup_parent.add_child(_popup)
|
||||
_popup.popup_centered(widget.host_window_size)
|
||||
_popup.add_child(widget)
|
||||
|
||||
return _popup
|
||||
|
||||
## Updates the local variables which point to the current and latest version of Loggie.
|
||||
func update_version_cache():
|
||||
# Read and cache the current version of Loggie from plugin.cfg.
|
||||
find_and_store_current_version()
|
||||
|
||||
# Read and cache the latest version of Loggie from GitHub.
|
||||
# (Do it only if running in editor, no need for this if running in a game).
|
||||
var logger = self.get_logger()
|
||||
if logger is Node:
|
||||
if !Engine.is_editor_hint():
|
||||
return
|
||||
if logger.is_node_ready():
|
||||
find_and_store_latest_version()
|
||||
else:
|
||||
logger.ready.connect(func():
|
||||
find_and_store_latest_version()
|
||||
, CONNECT_ONE_SHOT)
|
||||
|
||||
## Checks if an update for Loggie is available. Run only after running [method update_version_cache].
|
||||
func is_update_available() -> bool:
|
||||
var loggie = self.get_logger()
|
||||
if !(self.version is LoggieVersion and self.version.is_valid()):
|
||||
loggie.error("The current version of Loggie is not valid. Run `find_and_store_current_version` once to obtain this value first.")
|
||||
return false
|
||||
if !(self.latest_version is LoggieVersion and self.latest_version.is_valid()):
|
||||
loggie.error("The latest version of Loggie is not valid. Run `find_and_store_latest_version` once to obtain this value first.")
|
||||
return false
|
||||
return self.latest_version.is_higher_than(self.version)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://cdu8t2jbqqvb2
|
||||
192
addons/loggie/version_management/update_prompt_window.gd
Normal file
192
addons/loggie/version_management/update_prompt_window.gd
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
@tool
|
||||
## The Loggie Update Prompt is a control node that is meant to be created and added as a child of some other node, most commonly a [Window].
|
||||
## It connects to a [LoggieUpdate] via its [method connect_to_update] method, then displays data about that update depending on what kind of
|
||||
## data that [LoggieUpdate] provides with its signals.
|
||||
class_name LoggieUpdatePrompt extends Panel
|
||||
|
||||
## Emitted when the user requests to close the update prompt.
|
||||
signal close_requested()
|
||||
|
||||
## The animation player that will be used to animate the appearance of this window.
|
||||
@export var animator : AnimationPlayer
|
||||
|
||||
## The size the window that's hosting this panel will be forced to assume when
|
||||
## it's ready to pop this up on the screen.
|
||||
@export var host_window_size : Vector2 = Vector2(1063, 672)
|
||||
|
||||
## Stores a reference to the logger that's using this window.
|
||||
var _logger : Variant
|
||||
|
||||
## The update this window is visually representing.
|
||||
var _update : LoggieUpdate
|
||||
|
||||
## Stores a boolean which indicates whether the update is currently in progress.
|
||||
var is_currently_updating : bool = false
|
||||
|
||||
func _ready() -> void:
|
||||
connect_control_effects()
|
||||
%UpdateNowBtn.grab_focus()
|
||||
refresh_remind_later_btn()
|
||||
animator.play("RESET")
|
||||
|
||||
## Connects this window to an instance of [LoggieUpdate] whose progress and properties this window is supposed to track.
|
||||
func connect_to_update(p_update : LoggieUpdate) -> void:
|
||||
self._update = p_update
|
||||
_update.is_in_progress_changed.connect(is_update_in_progress_changed)
|
||||
_update.starting.connect(on_update_starting)
|
||||
_update.succeeded.connect(on_update_succeeded)
|
||||
_update.failed.connect(on_update_failed)
|
||||
_update.progress.connect(on_update_progress)
|
||||
_update.status_changed.connect(on_update_status_changed)
|
||||
|
||||
## Returns a reference to the logger object that is using this widget.
|
||||
func get_logger() -> Variant:
|
||||
return self._logger
|
||||
|
||||
## Defines what happens when the update this window is representing updates its "is in progress" status.
|
||||
func is_update_in_progress_changed(is_in_progress : bool) -> void:
|
||||
self.is_currently_updating = is_in_progress
|
||||
|
||||
# The first time we enter the UpdateMonitor view and start an update,
|
||||
# the %Notice node and its children should no longer (ever) be interactive or processing,
|
||||
# since there is no way to get back to that view anymore.
|
||||
if is_in_progress and %Notice.process_mode != Node.PROCESS_MODE_DISABLED:
|
||||
%Notice.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
for child in %NoticeButtons.get_children():
|
||||
if child is Button:
|
||||
child.focus_mode = Button.FOCUS_NONE
|
||||
|
||||
## Connects the effects and functionalities of various controls in this window.
|
||||
func connect_control_effects():
|
||||
if !is_instance_valid(self._update):
|
||||
return
|
||||
|
||||
# Configure version(s) labels.
|
||||
%LabelCurrentVersion.text = str(self._update.prev_version)
|
||||
%LabelLatestVersion.text = str(self._update.new_version)
|
||||
%LabelOldVersion.text = str(self._update.prev_version)
|
||||
%LabelNewVersion.text = str(self._update.new_version)
|
||||
|
||||
# Configure onhover/focused effects.
|
||||
var buttons_with_on_focushover_effect = [%OptionExitBtn, %OptionRestartGodotBtn, %OptionRetryUpdateBtn, %ReleaseNotesBtn, %RemindLaterBtn, %UpdateNowBtn]
|
||||
for button : Button in buttons_with_on_focushover_effect:
|
||||
button.focus_entered.connect(_on_button_focus_entered.bind(button))
|
||||
button.focus_exited.connect(_on_button_focus_exited.bind(button))
|
||||
button.mouse_entered.connect(_on_button_focus_entered.bind(button))
|
||||
button.mouse_exited.connect(_on_button_focus_exited.bind(button))
|
||||
button.pivot_offset = button.size * 0.5
|
||||
|
||||
# Connect behavior which executes when "Update Now" is pressed.
|
||||
%UpdateNowBtn.pressed.connect(func():
|
||||
self._update.try_start()
|
||||
)
|
||||
|
||||
# Configure Release Notes button.
|
||||
%ReleaseNotesBtn.visible = !self._update.release_notes_url.is_empty()
|
||||
%ReleaseNotesBtn.tooltip_text = "Opens {url} in browser.".format({"url": self._update.release_notes_url})
|
||||
%ReleaseNotesBtn.pressed.connect(func():
|
||||
if !self._update.release_notes_url.is_empty():
|
||||
OS.shell_open(self._update.release_notes_url)
|
||||
)
|
||||
|
||||
# Connect behavior which executes when the "Remind Me Later / Close" is pressed.
|
||||
%RemindLaterBtn.pressed.connect(func(): close_requested.emit())
|
||||
%OptionExitBtn.pressed.connect(func(): close_requested.emit())
|
||||
|
||||
# Connect behavior which executes when the "Retry" button is pressed.
|
||||
%OptionRetryUpdateBtn.pressed.connect(self._update.try_start)
|
||||
|
||||
# Connect behavior which executes when the "Restart Godot" button is pressed.
|
||||
%OptionRestartGodotBtn.pressed.connect(func():
|
||||
close_requested.emit()
|
||||
if Engine.is_editor_hint():
|
||||
var editor_plugin = Engine.get_meta("LoggieEditorPlugin")
|
||||
editor_plugin.get_editor_interface().restart_editor(true)
|
||||
)
|
||||
|
||||
# The "Don't show again checkbox" updates project settings whenever it is toggled.
|
||||
%DontShowAgainCheckbox.toggled.connect(func(toggled_on : bool):
|
||||
var loggie = self.get_logger()
|
||||
if Engine.is_editor_hint() and loggie != null:
|
||||
if toggled_on:
|
||||
loggie.settings.update_check_mode = LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_MSG
|
||||
ProjectSettings.set_setting(loggie.settings.project_settings.update_check_mode.path, LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_MSG)
|
||||
else:
|
||||
loggie.settings.update_check_mode = LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_UPDATER_WINDOW
|
||||
ProjectSettings.set_setting(loggie.settings.project_settings.update_check_mode.path, LoggieEnums.UpdateCheckType.CHECK_AND_SHOW_UPDATER_WINDOW)
|
||||
refresh_remind_later_btn()
|
||||
)
|
||||
|
||||
## Updates the content of the "Remind Later Btn" to show text appropriate to the current state of the "Don't show again" checkbox.
|
||||
func refresh_remind_later_btn():
|
||||
if %DontShowAgainCheckbox.button_pressed:
|
||||
%RemindLaterBtn.text = "close"
|
||||
else:
|
||||
%RemindLaterBtn.text = "remind later"
|
||||
|
||||
## Defines what happens when the [member _update] is detected to be starting.
|
||||
func on_update_starting():
|
||||
if animator.current_animation != "show_update_overview":
|
||||
animator.queue("show_update_overview")
|
||||
%ProgressBar.value = 0
|
||||
%LabelMainStatus.text = "Downloading"
|
||||
%LabelUpdateStatus.text = "Waiting for server response."
|
||||
%OptionExitBtn.disabled = true
|
||||
%OptionExitBtn.visible = false
|
||||
%OptionRetryUpdateBtn.disabled = true
|
||||
%OptionRetryUpdateBtn.visible = false
|
||||
%OptionRestartGodotBtn.disabled = true
|
||||
%OptionRestartGodotBtn.visible = false
|
||||
|
||||
## Defines what happens when the [member _update] declares it has made progress.
|
||||
func on_update_progress(value : float):
|
||||
%ProgressBar.value = value
|
||||
|
||||
## Defines what happens when the [member _update] declares it has succeeded.
|
||||
func on_update_succeeded():
|
||||
%LabelMainStatus.text = "Updated"
|
||||
%OptionExitBtn.disabled = false
|
||||
%OptionExitBtn.visible = true
|
||||
%OptionRestartGodotBtn.disabled = false
|
||||
%OptionRestartGodotBtn.visible = true
|
||||
if animator.is_playing():
|
||||
animator.stop()
|
||||
animator.play("finish_success")
|
||||
|
||||
## Defines what happens when the [member _update] declares it wants the status message to change.
|
||||
func on_update_status_changed(status_msg : Variant, substatus_msg : Variant):
|
||||
if status_msg is String:
|
||||
%LabelMainStatus.text = status_msg
|
||||
if substatus_msg is String:
|
||||
%LabelUpdateStatus.text = substatus_msg
|
||||
|
||||
## Defines what happens when the [member _update] declares it has failed.
|
||||
func on_update_failed():
|
||||
%ProgressBar.value = 0
|
||||
%LabelMainStatus.text = "Failed"
|
||||
%OptionExitBtn.disabled = false
|
||||
%OptionExitBtn.visible = true
|
||||
%OptionRetryUpdateBtn.disabled = false
|
||||
%OptionRetryUpdateBtn.visible = true
|
||||
%OptionRestartGodotBtn.disabled = true
|
||||
%OptionRestartGodotBtn.visible = false
|
||||
|
||||
func _on_button_focus_entered(button : Button):
|
||||
if button.has_meta("scale_tween"):
|
||||
var old_tween = button.get_meta("scale_tween")
|
||||
old_tween.kill()
|
||||
button.set_meta("scale_tween", null)
|
||||
|
||||
var tween : Tween = button.create_tween()
|
||||
tween.tween_property(button, "scale", Vector2(1.2, 1.2), 0.1)
|
||||
button.set_meta("scale_tween", tween)
|
||||
|
||||
func _on_button_focus_exited(button : Button):
|
||||
if button.has_meta("scale_tween"):
|
||||
var old_tween = button.get_meta("scale_tween")
|
||||
old_tween.kill()
|
||||
button.set_meta("scale_tween", null)
|
||||
|
||||
var tween : Tween = button.create_tween()
|
||||
tween.tween_property(button, "scale", Vector2(1.0, 1.0), 0.1).from_current()
|
||||
button.set_meta("scale_tween", tween)
|
||||
|
|
@ -0,0 +1 @@
|
|||
uid://dh7dlpputw3bg
|
||||
818
addons/loggie/version_management/update_prompt_window.tscn
Normal file
818
addons/loggie/version_management/update_prompt_window.tscn
Normal file
|
|
@ -0,0 +1,818 @@
|
|||
[gd_scene load_steps=32 format=3 uid="uid://d2eq0khfi3s15"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/loggie/version_management/update_prompt_window.gd" id="1_xb7je"]
|
||||
[ext_resource type="Texture2D" uid="uid://cgh6hd3u8nlpj" path="res://addons/loggie/assets/updater_bg.png" id="2_lbawe"]
|
||||
[ext_resource type="FontFile" uid="uid://e3rpni7mpu0p" path="res://addons/loggie/assets/theme/fonts/coffee_soda.ttf" id="3_a36jf"]
|
||||
[ext_resource type="FontFile" uid="uid://btuvtbhws7p8s" path="res://addons/loggie/assets/theme/fonts/PatrickHandSC-Regular.ttf" id="4_lnh27"]
|
||||
[ext_resource type="StyleBox" uid="uid://ckw36egxdynxc" path="res://addons/loggie/assets/theme/loggie_border_box.tres" id="5_avea8"]
|
||||
[ext_resource type="Texture2D" uid="uid://bfnp2a0sbhi2x" path="res://addons/loggie/assets/theme/checkbox/checkbox_checked.png" id="6_yoxpw"]
|
||||
[ext_resource type="Texture2D" uid="uid://bib1lwikra5kr" path="res://addons/loggie/assets/theme/checkbox/checkbox_unchecked.png" id="7_xu2hn"]
|
||||
[ext_resource type="Texture2D" uid="uid://dqf5cye21gyw8" path="res://addons/loggie/assets/theme/checkbox/checkbox_checked_disabled.png" id="8_4h2cx"]
|
||||
[ext_resource type="Texture2D" uid="uid://cloe7vx2ej0nf" path="res://addons/loggie/assets/theme/checkbox/checkbox_unchecked_disabled.png" id="9_51n7f"]
|
||||
[ext_resource type="Texture2D" uid="uid://2fr6et0qni2y" path="res://addons/loggie/assets/icon.png" id="10_abt8m"]
|
||||
[ext_resource type="Theme" uid="uid://bntkg3oi4b314" path="res://addons/loggie/assets/theme/loggie_theme.tres" id="11_5uxhl"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3bh8r"]
|
||||
draw_center = false
|
||||
border_width_bottom = 4
|
||||
border_color = Color(0.86, 0.6794, 0.344, 1)
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rjg43"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bwgrr"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_qbrxo"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_3asxb"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_86pfv"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rw12e"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_j1ttd"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_3sc8x"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ndum7"]
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_wnvcb"]
|
||||
colors = PackedColorArray(0.0311948, 0.0139167, 0.0080977, 1, 0.125625, 0.1125, 0.15, 1)
|
||||
|
||||
[sub_resource type="GradientTexture1D" id="GradientTexture1D_k1b6u"]
|
||||
gradient = SubResource("Gradient_wnvcb")
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_rkgx7"]
|
||||
interpolation_mode = 2
|
||||
offsets = PackedFloat32Array(0, 0.270059, 1)
|
||||
colors = PackedColorArray(0.584314, 0.337255, 0.145098, 1, 0.495303, 0.287021, 0.13981, 1, 0.188235, 0.113725, 0.121569, 1)
|
||||
|
||||
[sub_resource type="GradientTexture2D" id="GradientTexture2D_p6ad4"]
|
||||
gradient = SubResource("Gradient_rkgx7")
|
||||
fill_from = Vector2(0.239316, 0)
|
||||
fill_to = Vector2(0.303419, 0.876068)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_y40yc"]
|
||||
bg_color = Color(0.12, 0.0528, 0.09088, 0.556863)
|
||||
border_width_left = 3
|
||||
border_width_top = 3
|
||||
border_width_right = 3
|
||||
border_width_bottom = 6
|
||||
border_color = Color(0.533576, 0.418125, 0.23421, 1)
|
||||
border_blend = true
|
||||
corner_radius_top_left = 11
|
||||
corner_radius_top_right = 11
|
||||
corner_radius_bottom_right = 11
|
||||
corner_radius_bottom_left = 11
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h3ybw"]
|
||||
bg_color = Color(0.69, 0.49128, 0.2484, 0.486275)
|
||||
border_width_right = 4
|
||||
border_color = Color(0.94, 0.6768, 0.282, 0.541176)
|
||||
corner_radius_top_left = 18
|
||||
corner_radius_top_right = 18
|
||||
corner_radius_bottom_right = 18
|
||||
corner_radius_bottom_left = 18
|
||||
|
||||
[sub_resource type="Animation" id="Animation_almc7"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:position")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, -673)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath("VBoxContainer/OptionButtons/OptionRetryUpdateBtn:disabled")
|
||||
tracks/2/interp = 1
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [true]
|
||||
}
|
||||
tracks/3/type = "value"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath("VBoxContainer/OptionButtons/OptionExitBtn:disabled")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [true]
|
||||
}
|
||||
tracks/4/type = "value"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath("VBoxContainer/OptionButtons/OptionRetryUpdateBtn:visible")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/5/type = "value"
|
||||
tracks/5/imported = false
|
||||
tracks/5/enabled = true
|
||||
tracks/5/path = NodePath("VBoxContainer/OptionButtons/OptionExitBtn:visible")
|
||||
tracks/5/interp = 1
|
||||
tracks/5/loop_wrap = true
|
||||
tracks/5/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [false]
|
||||
}
|
||||
tracks/6/type = "value"
|
||||
tracks/6/imported = false
|
||||
tracks/6/enabled = true
|
||||
tracks/6/path = NodePath("%LabelOldVersion:modulate")
|
||||
tracks/6/interp = 1
|
||||
tracks/6/loop_wrap = true
|
||||
tracks/6/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
tracks/7/type = "value"
|
||||
tracks/7/imported = false
|
||||
tracks/7/enabled = true
|
||||
tracks/7/path = NodePath("%LabelNewVersion:modulate")
|
||||
tracks/7/interp = 1
|
||||
tracks/7/loop_wrap = true
|
||||
tracks/7/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
tracks/8/type = "value"
|
||||
tracks/8/imported = false
|
||||
tracks/8/enabled = true
|
||||
tracks/8/path = NodePath("%ProgressBar:modulate")
|
||||
tracks/8/interp = 1
|
||||
tracks/8/loop_wrap = true
|
||||
tracks/8/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
tracks/9/type = "value"
|
||||
tracks/9/imported = false
|
||||
tracks/9/enabled = true
|
||||
tracks/9/path = NodePath("VBoxContainer:position")
|
||||
tracks/9/interp = 1
|
||||
tracks/9/loop_wrap = true
|
||||
tracks/9/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, 532)]
|
||||
}
|
||||
tracks/10/type = "value"
|
||||
tracks/10/imported = false
|
||||
tracks/10/enabled = true
|
||||
tracks/10/path = NodePath("LoggieIcon:scale")
|
||||
tracks/10/interp = 1
|
||||
tracks/10/loop_wrap = true
|
||||
tracks/10/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(1, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_dewq5"]
|
||||
resource_name = "finish_success"
|
||||
length = 0.5
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:position")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, 0)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [true]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath("%LabelOldVersion:modulate")
|
||||
tracks/2/interp = 1
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
|
||||
}
|
||||
tracks/3/type = "value"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath("%LabelNewVersion:modulate")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
|
||||
}
|
||||
tracks/4/type = "value"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath("%ProgressBar:modulate")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
|
||||
}
|
||||
tracks/5/type = "value"
|
||||
tracks/5/imported = false
|
||||
tracks/5/enabled = true
|
||||
tracks/5/path = NodePath("VBoxContainer:position")
|
||||
tracks/5/interp = 1
|
||||
tracks/5/loop_wrap = true
|
||||
tracks/5/keys = {
|
||||
"times": PackedFloat32Array(0, 0.466667),
|
||||
"transitions": PackedFloat32Array(1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, 532), Vector2(1, 432)]
|
||||
}
|
||||
tracks/6/type = "value"
|
||||
tracks/6/imported = false
|
||||
tracks/6/enabled = true
|
||||
tracks/6/path = NodePath("LoggieIcon:scale")
|
||||
tracks/6/interp = 1
|
||||
tracks/6/loop_wrap = true
|
||||
tracks/6/keys = {
|
||||
"times": PackedFloat32Array(0, 0.166667, 0.2, 0.3, 0.366667, 0.433333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector2(1, 1), Vector2(1, 1), Vector2(1.33, 1), Vector2(1.075, 1.575), Vector2(0.735, 1.465), Vector2(1, 1)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_jm1cu"]
|
||||
resource_name = "show_update_overview"
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:position")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.2, 0.266667, 0.333333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, -673), Vector2(0, 0), Vector2(0, 16), Vector2(0, 0)]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:visible")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [true]
|
||||
}
|
||||
tracks/2/type = "value"
|
||||
tracks/2/imported = false
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath("%ProgressBar:modulate")
|
||||
tracks/2/interp = 1
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
tracks/3/type = "value"
|
||||
tracks/3/imported = false
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath("%LabelOldVersion:modulate")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
tracks/4/type = "value"
|
||||
tracks/4/imported = false
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath("%LabelNewVersion:modulate")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Color(1, 1, 1, 1)]
|
||||
}
|
||||
tracks/5/type = "value"
|
||||
tracks/5/imported = false
|
||||
tracks/5/enabled = true
|
||||
tracks/5/path = NodePath("LoggieIcon:scale")
|
||||
tracks/5/interp = 1
|
||||
tracks/5/loop_wrap = true
|
||||
tracks/5/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(1, 1)]
|
||||
}
|
||||
tracks/6/type = "value"
|
||||
tracks/6/imported = false
|
||||
tracks/6/enabled = true
|
||||
tracks/6/path = NodePath("VBoxContainer:position")
|
||||
tracks/6/interp = 1
|
||||
tracks/6/loop_wrap = true
|
||||
tracks/6/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, 532)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_lwiu5"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_almc7"),
|
||||
"finish_success": SubResource("Animation_dewq5"),
|
||||
"show_update_overview": SubResource("Animation_jm1cu")
|
||||
}
|
||||
|
||||
[node name="UpdatePromptWindow" type="Panel" node_paths=PackedStringArray("animator")]
|
||||
anchors_preset = -1
|
||||
anchor_right = 0.923
|
||||
anchor_bottom = 1.037
|
||||
offset_right = -0.296021
|
||||
offset_bottom = 0.0239868
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_xb7je")
|
||||
animator = NodePath("AnimationPlayer")
|
||||
|
||||
[node name="Notice" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Background" type="TextureRect" parent="Notice"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = ExtResource("2_lbawe")
|
||||
expand_mode = 1
|
||||
|
||||
[node name="LabelLatestVersion" type="Label" parent="Notice"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.148
|
||||
anchor_top = 0.549
|
||||
anchor_right = 0.352
|
||||
anchor_bottom = 0.735
|
||||
offset_left = 0.414978
|
||||
offset_top = 0.115936
|
||||
offset_right = -0.122009
|
||||
offset_bottom = 0.123932
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
theme_override_colors/font_color = Color(0.878431, 0.662745, 0.266667, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/shadow_offset_x = 3
|
||||
theme_override_constants/shadow_offset_y = 3
|
||||
theme_override_constants/outline_size = 18
|
||||
theme_override_constants/shadow_outline_size = 3
|
||||
theme_override_fonts/font = ExtResource("3_a36jf")
|
||||
theme_override_font_sizes/font_size = 133
|
||||
text = "X.Y"
|
||||
|
||||
[node name="LabelCurrentVersion" type="Label" parent="Notice"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.76
|
||||
anchor_top = 0.454
|
||||
anchor_right = 0.869
|
||||
anchor_bottom = 0.571
|
||||
offset_left = 0.0449829
|
||||
offset_top = 0.18399
|
||||
offset_right = 57.004
|
||||
offset_bottom = -0.107056
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(0.784314, 0.619608, 0.317647, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/shadow_offset_x = 3
|
||||
theme_override_constants/shadow_offset_y = 3
|
||||
theme_override_constants/outline_size = 18
|
||||
theme_override_constants/shadow_outline_size = 3
|
||||
theme_override_fonts/font = ExtResource("3_a36jf")
|
||||
theme_override_font_sizes/font_size = 73
|
||||
text = "X.Y"
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Notice"]
|
||||
layout_mode = 1
|
||||
anchors_preset = -1
|
||||
anchor_left = 0.229
|
||||
anchor_top = 0.789
|
||||
anchor_right = 0.771
|
||||
anchor_bottom = 0.926
|
||||
offset_left = 0.0729828
|
||||
offset_top = -0.208008
|
||||
offset_right = -0.0730591
|
||||
offset_bottom = 9.72797
|
||||
grow_horizontal = 2
|
||||
theme_override_constants/separation = -15
|
||||
|
||||
[node name="Label" type="Label" parent="Notice/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
theme_override_colors/font_color = Color(0.872047, 0.774098, 0.671572, 1)
|
||||
theme_override_constants/outline_size = 7
|
||||
theme_override_fonts/font = ExtResource("4_lnh27")
|
||||
theme_override_font_sizes/font_size = 32
|
||||
text = "a new version of Loggie is available for download."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Notice/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
alignment = 1
|
||||
|
||||
[node name="NoticeButtons" type="HBoxContainer" parent="Notice/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 6
|
||||
theme_override_constants/separation = 51
|
||||
|
||||
[node name="ReleaseNotesBtn" type="Button" parent="Notice/VBoxContainer/HBoxContainer/NoticeButtons"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme_override_colors/font_disabled_color = Color(0.53, 0.475145, 0.3869, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0.996078, 0.94902, 0.882353, 1)
|
||||
theme_override_colors/font_hover_color = Color(0.98, 0.67571, 0.1862, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.994326, 0.950716, 0.88154, 1)
|
||||
theme_override_colors/font_focus_color = Color(0.996078, 0.94902, 0.882353, 1)
|
||||
theme_override_colors/font_color = Color(0.980932, 0.843799, 0.621104, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 8
|
||||
theme_override_fonts/font = ExtResource("4_lnh27")
|
||||
theme_override_font_sizes/font_size = 43
|
||||
theme_override_styles/focus = SubResource("StyleBoxFlat_3bh8r")
|
||||
theme_override_styles/disabled_mirrored = SubResource("StyleBoxEmpty_rjg43")
|
||||
theme_override_styles/disabled = SubResource("StyleBoxEmpty_bwgrr")
|
||||
theme_override_styles/hover_pressed_mirrored = SubResource("StyleBoxEmpty_qbrxo")
|
||||
theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_3asxb")
|
||||
theme_override_styles/hover_mirrored = SubResource("StyleBoxEmpty_86pfv")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_3bh8r")
|
||||
theme_override_styles/pressed_mirrored = SubResource("StyleBoxEmpty_rw12e")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxEmpty_j1ttd")
|
||||
theme_override_styles/normal_mirrored = SubResource("StyleBoxEmpty_3sc8x")
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_ndum7")
|
||||
text = "release notes"
|
||||
|
||||
[node name="UpdateNowBtn" type="Button" parent="Notice/VBoxContainer/HBoxContainer/NoticeButtons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme_override_colors/font_disabled_color = Color(0.53, 0.475145, 0.3869, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0.996078, 0.94902, 0.882353, 1)
|
||||
theme_override_colors/font_hover_color = Color(0.98, 0.67571, 0.1862, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.994326, 0.950716, 0.88154, 1)
|
||||
theme_override_colors/font_focus_color = Color(0.996078, 0.94902, 0.882353, 1)
|
||||
theme_override_colors/font_color = Color(0.980932, 0.843799, 0.621104, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 8
|
||||
theme_override_fonts/font = ExtResource("4_lnh27")
|
||||
theme_override_font_sizes/font_size = 43
|
||||
theme_override_styles/focus = SubResource("StyleBoxFlat_3bh8r")
|
||||
theme_override_styles/disabled_mirrored = SubResource("StyleBoxEmpty_rjg43")
|
||||
theme_override_styles/disabled = SubResource("StyleBoxEmpty_bwgrr")
|
||||
theme_override_styles/hover_pressed_mirrored = SubResource("StyleBoxEmpty_qbrxo")
|
||||
theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_3asxb")
|
||||
theme_override_styles/hover_mirrored = SubResource("StyleBoxEmpty_86pfv")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_3bh8r")
|
||||
theme_override_styles/pressed_mirrored = SubResource("StyleBoxEmpty_rw12e")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxEmpty_j1ttd")
|
||||
theme_override_styles/normal_mirrored = SubResource("StyleBoxEmpty_3sc8x")
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_ndum7")
|
||||
text = "update now"
|
||||
|
||||
[node name="RemindLaterBtn" type="Button" parent="Notice/VBoxContainer/HBoxContainer/NoticeButtons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme_override_colors/font_disabled_color = Color(0.53, 0.475145, 0.3869, 1)
|
||||
theme_override_colors/font_hover_pressed_color = Color(0.996078, 0.94902, 0.882353, 1)
|
||||
theme_override_colors/font_hover_color = Color(0.98, 0.67571, 0.1862, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.994326, 0.950716, 0.88154, 1)
|
||||
theme_override_colors/font_focus_color = Color(0.996078, 0.94902, 0.882353, 1)
|
||||
theme_override_colors/font_color = Color(0.98, 0.49, 0.5635, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 8
|
||||
theme_override_fonts/font = ExtResource("4_lnh27")
|
||||
theme_override_font_sizes/font_size = 43
|
||||
theme_override_styles/focus = SubResource("StyleBoxFlat_3bh8r")
|
||||
theme_override_styles/disabled_mirrored = SubResource("StyleBoxEmpty_rjg43")
|
||||
theme_override_styles/disabled = SubResource("StyleBoxEmpty_bwgrr")
|
||||
theme_override_styles/hover_pressed_mirrored = SubResource("StyleBoxEmpty_qbrxo")
|
||||
theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_3asxb")
|
||||
theme_override_styles/hover_mirrored = SubResource("StyleBoxEmpty_86pfv")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_3bh8r")
|
||||
theme_override_styles/pressed_mirrored = SubResource("StyleBoxEmpty_rw12e")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxEmpty_j1ttd")
|
||||
theme_override_styles/normal_mirrored = SubResource("StyleBoxEmpty_3sc8x")
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_ndum7")
|
||||
text = "remind later"
|
||||
|
||||
[node name="DontShowAgainCheckbox" type="CheckBox" parent="Notice"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 1
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
offset_left = -289.0
|
||||
offset_top = 9.0
|
||||
offset_right = -20.0
|
||||
offset_bottom = 62.0
|
||||
grow_horizontal = 0
|
||||
mouse_default_cursor_shape = 2
|
||||
theme_override_colors/font_hover_color = Color(0.490196, 0.701961, 0.501961, 1)
|
||||
theme_override_colors/font_focus_color = Color(0.92549, 0.92549, 0.756863, 1)
|
||||
theme_override_colors/font_color = Color(0.980932, 0.843799, 0.621104, 1)
|
||||
theme_override_constants/outline_size = 7
|
||||
theme_override_fonts/font = ExtResource("4_lnh27")
|
||||
theme_override_font_sizes/font_size = 27
|
||||
theme_override_styles/focus = ExtResource("5_avea8")
|
||||
theme_override_icons/checked = ExtResource("6_yoxpw")
|
||||
theme_override_icons/unchecked = ExtResource("7_xu2hn")
|
||||
theme_override_icons/radio_checked = ExtResource("6_yoxpw")
|
||||
theme_override_icons/radio_unchecked = ExtResource("7_xu2hn")
|
||||
theme_override_icons/checked_disabled = ExtResource("8_4h2cx")
|
||||
theme_override_icons/unchecked_disabled = ExtResource("9_51n7f")
|
||||
theme_override_icons/radio_checked_disabled = ExtResource("8_4h2cx")
|
||||
theme_override_icons/radio_unchecked_disabled = ExtResource("9_51n7f")
|
||||
text = "do not show this again"
|
||||
|
||||
[node name="UpdateMonitor" type="Control" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -673.0
|
||||
offset_bottom = -673.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="BackgroundUnder" type="TextureRect" parent="UpdateMonitor"]
|
||||
modulate = Color(1, 1, 1, 0.811765)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("GradientTexture1D_k1b6u")
|
||||
|
||||
[node name="BackgroundOver" type="TextureRect" parent="UpdateMonitor"]
|
||||
modulate = Color(1, 1, 1, 0.752941)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
texture = SubResource("GradientTexture2D_p6ad4")
|
||||
|
||||
[node name="ProgressBar" type="ProgressBar" parent="UpdateMonitor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 7
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -448.5
|
||||
offset_top = -224.0
|
||||
offset_right = 449.5
|
||||
offset_bottom = -160.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
theme_override_constants/outline_size = 19
|
||||
theme_override_fonts/font = ExtResource("3_a36jf")
|
||||
theme_override_font_sizes/font_size = 31
|
||||
theme_override_styles/background = SubResource("StyleBoxFlat_y40yc")
|
||||
theme_override_styles/fill = SubResource("StyleBoxFlat_h3ybw")
|
||||
|
||||
[node name="LabelMainStatus" type="Label" parent="UpdateMonitor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -201.0
|
||||
offset_top = -13.0
|
||||
offset_right = 201.0
|
||||
offset_bottom = 63.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(0.878431, 0.662745, 0.266667, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 14
|
||||
theme_override_fonts/font = ExtResource("3_a36jf")
|
||||
theme_override_font_sizes/font_size = 81
|
||||
text = "status"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="LabelOldVersion" type="Label" parent="UpdateMonitor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -426.5
|
||||
offset_top = 77.0
|
||||
offset_right = -24.5
|
||||
offset_bottom = 153.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(0.878431, 0.662745, 0.266667, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 14
|
||||
theme_override_fonts/font = ExtResource("3_a36jf")
|
||||
theme_override_font_sizes/font_size = 25
|
||||
text = "v_old"
|
||||
|
||||
[node name="LabelNewVersion" type="Label" parent="UpdateMonitor"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = 33.5
|
||||
offset_top = 76.0
|
||||
offset_right = 435.5
|
||||
offset_bottom = 152.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(0.878431, 0.662745, 0.266667, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 14
|
||||
theme_override_fonts/font = ExtResource("3_a36jf")
|
||||
theme_override_font_sizes/font_size = 25
|
||||
text = "V_NEW"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="LoggieIcon" type="TextureRect" parent="UpdateMonitor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -79.5
|
||||
offset_top = -213.0
|
||||
offset_right = 80.5
|
||||
offset_bottom = -53.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
pivot_offset = Vector2(80, 84)
|
||||
texture = ExtResource("10_abt8m")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UpdateMonitor"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 12
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -140.0
|
||||
offset_bottom = 77.0002
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="OptionButtons" type="HBoxContainer" parent="UpdateMonitor/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 24
|
||||
alignment = 1
|
||||
|
||||
[node name="OptionRetryUpdateBtn" type="Button" parent="UpdateMonitor/VBoxContainer/OptionButtons"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme = ExtResource("11_5uxhl")
|
||||
disabled = true
|
||||
text = "Retry"
|
||||
|
||||
[node name="OptionRestartGodotBtn" type="Button" parent="UpdateMonitor/VBoxContainer/OptionButtons"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme = ExtResource("11_5uxhl")
|
||||
disabled = true
|
||||
text = "Reload Godot"
|
||||
|
||||
[node name="OptionExitBtn" type="Button" parent="UpdateMonitor/VBoxContainer/OptionButtons"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
mouse_default_cursor_shape = 2
|
||||
theme = ExtResource("11_5uxhl")
|
||||
theme_override_colors/font_color = Color(0.980919, 0.500031, 0.570874, 1)
|
||||
theme_override_colors/font_outline_color = Color(0.224555, 0.120107, 0.0255992, 1)
|
||||
theme_override_constants/outline_size = 8
|
||||
disabled = true
|
||||
text = "Exit"
|
||||
|
||||
[node name="LabelUpdateStatus" type="Label" parent="UpdateMonitor/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme = ExtResource("11_5uxhl")
|
||||
theme_override_font_sizes/font_size = 20
|
||||
text = "UPDATE_STATUS_DETAILED"
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
root_node = NodePath("../UpdateMonitor")
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_lwiu5")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue