class_name UpdatePopup extends BasePopup ## Gets the newest version of the game, opened from "Version X is available" in the top ## bar. ## ## Update and restart downloads the new game, puts it in place of this one and starts it; ## the quiz is saved first, so it continues where it was. Where the game can't update ## itself, e.g. when it runs from the editor, only Download in browser is offered, which ## opens the download (or, on other platforms, the release page) in the browser. ## Emitted just before the game quits to start the new version, so the quiz can be saved. signal restarting ## Space between the rows and the panel's edges. const PADDING: Vector2 = Vector2(24, 16) ## Opens a link in the browser. The tests replace it, so no browser opens. var open_url: Callable = OS.shell_open ## The version offered. var latest_version: String = "" var _downloading: bool = false # Where the panel's middle is, so it stays centred as it grows and shrinks var _panel_middle_y: float = 0.0 @onready var title_label: Label = %TitleLabel @onready var info_label: Label = %InfoLabel @onready var reason_label: Label = %ReasonLabel @onready var download_progress: ProgressBar = %DownloadProgress @onready var status_label: Label = %StatusLabel @onready var not_now_button: Button = %NotNowButton @onready var browser_button: Button = %BrowserButton @onready var update_now_button: Button = %UpdateNowButton @onready var updater: Updater = %Updater @onready var rows: VBoxContainer = %Rows func _ready() -> void: not_now_button.pressed.connect(close) browser_button.pressed.connect(download_in_browser) update_now_button.pressed.connect(update_now) _panel_middle_y = panel.position.y + panel.size.y / 2 ## Offers version [param latest] and opens the popup. func show_update(latest: String) -> void: latest_version = latest title_label.text = "Version %s is available" % latest info_label.text = "You have %s. The game restarts with the new version, and the quiz " \ % Settings.version + "continues where it was." var reason: String = updater.can_update_in_place() update_now_button.visible = reason.is_empty() reason_label.visible = !reason.is_empty() reason_label.text = reason + " Download it in the browser instead." info_label.visible = reason.is_empty() download_progress.visible = false status_label.visible = false _set_busy(false) _fit_to_rows() open() ## Stops a download that is under way when the popup is closed. func close() -> void: if _downloading: updater.cancel() _downloading = false _set_busy(false) super() ## Downloads the new version, puts it in place and restarts the game. func update_now() -> void: _set_busy(true) _show_status("Getting the download link…") MusicServerClient.fetch_client_download_url(Updater.asset_for(updater.os_name, true), _download, _on_failed.bind("The server has no download for this version.")) ## Opens the new version's download in the browser, or the release page when there is ## none for this platform. func download_in_browser() -> void: var asset: String = Updater.asset_for(updater.os_name, false) if asset.is_empty(): open_url.call(Updater.RELEASES_PAGE) return _show_status("Opening the download in the browser…") MusicServerClient.fetch_client_download_url(asset, func(url: String) -> void: open_url.call(url), func() -> void: open_url.call(Updater.RELEASES_PAGE)) func _download(url: String) -> void: _downloading = true download_progress.value = 0.0 download_progress.visible = true _show_status("Downloading…") _fit_to_rows() updater.download(url, _on_progress, _install, _on_failed) func _on_progress(done: int, total: int) -> void: if total > 0: download_progress.value = float(done) / float(total) status_label.text = "Downloading… %s of %s" % [SettingsPopup.size_text(done), SettingsPopup.size_text(total)] func _install(path: String) -> void: _downloading = false _show_status("Installing…") var error: Error = Updater.install(path, updater.executable_path, updater.os_name) if error != OK: DirAccess.remove_absolute(path) _on_failed("The game could not be replaced: %s." % error_string(error)) return _show_status("Restarting…") restarting.emit() updater.restart() func _on_failed(reason: String) -> void: _downloading = false download_progress.visible = false _set_busy(false) _show_status(reason) _fit_to_rows() func _set_busy(busy: bool) -> void: update_now_button.disabled = busy browser_button.disabled = busy # Closing while busy stops the download not_now_button.text = "Cancel" if busy else "Not now" func _show_status(message: String) -> void: status_label.text = message if !status_label.visible: status_label.visible = true _fit_to_rows() # Makes the panel as tall as its rows, around the same middle func _fit_to_rows() -> void: # A popup that was never shown hasn't laid out its rows, and wrapped text measures # its height from its width var width: float = panel.size.x - PADDING.x * 2 for label: Label in [info_label, reason_label, status_label]: label.size = Vector2(width, 0) label.update_minimum_size() var height: float = rows.get_combined_minimum_size().y + PADDING.y * 2 panel.size.y = height panel.position.y = floorf(_panel_middle_y - height / 2) rows.position = PADDING rows.size = Vector2(panel.size.x - PADDING.x * 2, height - PADDING.y * 2)