diff --git a/src/Core/ChangeInformation.vala b/src/Core/ChangeInformation.vala index f4a00dc9f..c91b0e3d2 100644 --- a/src/Core/ChangeInformation.vala +++ b/src/Core/ChangeInformation.vala @@ -24,85 +24,44 @@ public class AppCenterCore.ChangeInformation : Object { UNKNOWN, CANCELLED, WAITING, - RUNNING, - FINISHED + RUNNING } - /** - * This signal is likely to be fired from a non-main thread. Ensure any UI - * logic driven from this runs on the GTK thread - */ - public signal void status_changed (); - - /** - * This signal is likely to be fired from a non-main thread. Ensure any UI - * logic driven from this runs on the GTK thread - */ - public signal void progress_changed (); - - public Gee.ArrayList updatable_packages { get; private set; } - - public bool can_cancel { public get; private set; default=true; } - public double progress { public get; private set; default=0.0f; } - public Status status { public get; private set; default=Status.UNKNOWN; } - public string status_description { public get; private set; default=_("Waiting"); } - public uint64 size; - - construct { - updatable_packages = new Gee.ArrayList (); - size = 0; - } - - public bool has_changes () { - return updatable_packages.size > 0; - } + public Cancellable cancellable { get; private set; } + public bool can_cancel { get; private set; default = true; } + public double progress { get; private set; default = 0.0f; } + public Status status { get; private set; default = Status.UNKNOWN; } + public string status_description { get; private set; default = _("Waiting"); } public void start () { - progress = 0.0f; + cancellable = new Cancellable (); + can_cancel = true; status = Status.WAITING; status_description = _("Waiting"); - status_changed (); - progress_changed (); - } - - public void complete () { - status = Status.FINISHED; - status_description = _("Finished"); - status_changed (); - reset_progress (); } public void cancel () { - progress = 0.0f; status = Status.CANCELLED; status_description = _("Cancelling"); - reset_progress (); - status_changed (); - progress_changed (); } - public void clear_update_info () { - updatable_packages.clear (); - size = 0; - } - - public void reset_progress () { + public void complete () { + can_cancel = false; + progress = 0; status = Status.UNKNOWN; - status_description = _("Starting"); - progress = 0.0f; + status_description = _("Unknown"); } - public void callback (bool can_cancel, string status_description, double progress, Status status) { - if (this.can_cancel != can_cancel || this.status_description != status_description || this.status != status) { - this.can_cancel = can_cancel; - this.status_description = status_description; - this.status = status; - status_changed (); - } + public void callback (double progress, string status_description) { + Idle.add_once (() => idle_callback (progress, status_description)); + } - if (this.progress != progress) { - this.progress = progress; - progress_changed (); + private void idle_callback (double progress, string status_description) { + if (status != RUNNING) { + status = RUNNING; } + + this.progress = progress; + this.status_description = status_description; } } diff --git a/src/Core/FlatpakBackend.vala b/src/Core/FlatpakBackend.vala index c82a4776f..27c44c23a 100644 --- a/src/Core/FlatpakBackend.vala +++ b/src/Core/FlatpakBackend.vala @@ -111,7 +111,7 @@ public class AppCenterCore.FlatpakBackend : Object { uint64 size = 0; for (uint i = 0; i < updatable_packages.get_n_items (); i++) { var package = (Package) updatable_packages.get_item (i); - size += package.change_information.size; + size += package.update_information.size; } return size; } @@ -1623,8 +1623,8 @@ public class AppCenterCore.FlatpakBackend : Object { unowned var args = (InstallPackageArgs)job.args; unowned var package = args.package; unowned var fp_package = package as FlatpakPackage; - unowned ChangeInformation? change_info = args.change_info; - unowned var cancellable = args.cancellable; + unowned var change_info = args.change_info; + unowned var cancellable = change_info.cancellable; var bundle = package.component.get_bundle (AppStream.BundleKind.FLATPAK); if (bundle == null) { @@ -1683,7 +1683,7 @@ public class AppCenterCore.FlatpakBackend : Object { // Calculate the progress contribution of the previous operations not including the current, hence -1 double existing_progress = (double)(current_operation - 1) / (double)total_operations; double this_op_progress = (double)progress.get_progress () / 100.0f / (double)total_operations; - change_info.callback (true, _("Installing"), existing_progress + this_op_progress, ChangeInformation.Status.RUNNING); + change_info.callback (existing_progress + this_op_progress, _("Installing")); }); }); @@ -1692,7 +1692,6 @@ public class AppCenterCore.FlatpakBackend : Object { transaction.operation_error.connect ((operation, e, detail) => { warning ("Flatpak installation failed: %s (detail: %d)", e.message, detail); if (e is GLib.IOError.CANCELLED) { - change_info.callback (false, _("Cancelling"), 1.0f, ChangeInformation.Status.CANCELLED); success = true; } @@ -1716,7 +1715,6 @@ public class AppCenterCore.FlatpakBackend : Object { success = transaction.run (cancellable); } catch (Error e) { if (e is GLib.IOError.CANCELLED) { - change_info.callback (false, _("Cancelling"), 1.0f, ChangeInformation.Status.CANCELLED); success = true; } else { success = false; @@ -1732,11 +1730,10 @@ public class AppCenterCore.FlatpakBackend : Object { job.results_ready (); } - public async bool install_package (Package package, ChangeInformation? change_info, Cancellable? cancellable) throws GLib.Error { + public async bool install_package (Package package, ChangeInformation change_info) throws GLib.Error { var job_args = new InstallPackageArgs (); job_args.package = package; job_args.change_info = change_info; - job_args.cancellable = cancellable; var job = yield launch_job (Job.Type.INSTALL_PACKAGE, job_args); if (job.error != null) { @@ -1750,8 +1747,8 @@ public class AppCenterCore.FlatpakBackend : Object { unowned var args = (RemovePackageArgs)job.args; unowned var package = args.package; unowned var fp_package = package as FlatpakPackage; - unowned ChangeInformation? change_info = args.change_info; - unowned var cancellable = args.cancellable; + unowned var change_info = args.change_info; + unowned var cancellable = change_info.cancellable; unowned var bundle = package.component.get_bundle (AppStream.BundleKind.FLATPAK); if (bundle == null) { @@ -1815,7 +1812,7 @@ public class AppCenterCore.FlatpakBackend : Object { // Calculate the progress contribution of the previous operations not including the current, hence -1 double existing_progress = (double)(current_operation - 1) / (double)total_operations; double this_op_progress = (double)progress.get_progress () / 100.0f / (double)total_operations; - change_info.callback (true, _("Uninstalling"), existing_progress + this_op_progress, ChangeInformation.Status.RUNNING); + change_info.callback (existing_progress + this_op_progress, _("Uninstalling")); }); }); @@ -1824,7 +1821,6 @@ public class AppCenterCore.FlatpakBackend : Object { transaction.operation_error.connect ((operation, e, detail) => { warning ("Flatpak removal failed: %s (detail: %d)", e.message, detail); if (e is GLib.IOError.CANCELLED) { - change_info.callback (false, _("Cancelling"), 1.0f, ChangeInformation.Status.CANCELLED); success = true; } @@ -1848,7 +1844,6 @@ public class AppCenterCore.FlatpakBackend : Object { success = transaction.run (cancellable); } catch (Error e) { if (e is GLib.IOError.CANCELLED) { - change_info.callback (false, _("Cancelling"), 1.0f, ChangeInformation.Status.CANCELLED); success = true; } else { success = false; @@ -1864,11 +1859,10 @@ public class AppCenterCore.FlatpakBackend : Object { job.results_ready (); } - public async bool remove_package (Package package, ChangeInformation? change_info, Cancellable? cancellable) throws GLib.Error { + public async bool remove_package (Package package, ChangeInformation change_info) throws GLib.Error { var job_args = new RemovePackageArgs (); job_args.package = package; job_args.change_info = change_info; - job_args.cancellable = cancellable; var job = yield launch_job (Job.Type.REMOVE_PACKAGE, job_args); if (job.error != null) { @@ -1881,8 +1875,8 @@ public class AppCenterCore.FlatpakBackend : Object { private void update_package_internal (Job job) { unowned var args = (UpdatePackageArgs)job.args; unowned var package = args.package; - unowned ChangeInformation change_info = args.change_info; - unowned var cancellable = args.cancellable; + unowned var change_info = args.change_info; + unowned var cancellable = change_info.cancellable; if (user_installation == null && system_installation == null) { critical ("Error getting flatpak installation"); @@ -1894,7 +1888,7 @@ public class AppCenterCore.FlatpakBackend : Object { string[] user_updates = {}; string[] system_updates = {}; - foreach (var updatable in package.change_information.updatable_packages) { + foreach (var updatable in package.update_information.updatable_packages) { bool system = false; string bundle_id = ""; @@ -1997,7 +1991,7 @@ public class AppCenterCore.FlatpakBackend : Object { // Calculate the progress contribution of the previous operations not including the current, hence -1 double existing_progress = (double)(current_operation - 1) / (double)total_operations; double this_op_progress = (double)progress.get_progress () / 100.0f / (double)total_operations; - change_info.callback (true, _("Updating"), existing_progress + this_op_progress, ChangeInformation.Status.RUNNING); + change_info.callback (existing_progress + this_op_progress, _("Updating")); }); }); @@ -2006,7 +2000,6 @@ public class AppCenterCore.FlatpakBackend : Object { transaction.operation_error.connect ((operation, e, detail) => { warning ("Flatpak installation failed: %s", e.message); if (e is GLib.IOError.CANCELLED) { - change_info.callback (false, _("Cancelling"), 1.0f, ChangeInformation.Status.CANCELLED); success = true; } @@ -2024,7 +2017,6 @@ public class AppCenterCore.FlatpakBackend : Object { success = transaction.run (cancellable); } catch (Error e) { if (e is GLib.IOError.CANCELLED) { - change_info.callback (false, _("Cancelling"), 1.0f, ChangeInformation.Status.CANCELLED); success = true; } else { throw e; @@ -2034,11 +2026,10 @@ public class AppCenterCore.FlatpakBackend : Object { return success; } - public async bool update_package (Package package, ChangeInformation? change_info, Cancellable? cancellable) throws GLib.Error { + public async bool update_package (Package package, ChangeInformation change_info) throws GLib.Error { var job_args = new UpdatePackageArgs (); job_args.package = package; job_args.change_info = change_info; - job_args.cancellable = cancellable; var job = yield launch_job (Job.Type.UPDATE_PACKAGE, job_args); if (job.error != null) { @@ -2103,14 +2094,14 @@ public class AppCenterCore.FlatpakBackend : Object { return; } - private void fill_runtime_updates () { - if (!runtime_updates.update_available) { + private void fill_runtime_updates (UpdateInformation info) { + if (info.updatable_packages.is_empty) { return; } string runtime_desc = ""; - foreach (var update in runtime_updates.change_information.updatable_packages) { + foreach (var update in info.updatable_packages) { string bundle_id; if (!get_package_list_key_parts (update, null, null, out bundle_id)) { continue; @@ -2134,10 +2125,12 @@ public class AppCenterCore.FlatpakBackend : Object { var latest_version = ngettext ( "%u runtime with updates", "%u runtimes with updates", - runtime_updates.change_information.updatable_packages.size - ).printf (runtime_updates.change_information.updatable_packages.size); + info.updatable_packages.size + ).printf (info.updatable_packages.size); runtime_updates.latest_version = latest_version; runtime_updates.description = "%s\n%s\n".printf (GLib.Markup.printf_escaped (_("%s:"), latest_version), runtime_desc); + + runtime_updates.update_information = info; } public async void get_updates (Cancellable? cancellable = null) { @@ -2147,27 +2140,34 @@ public class AppCenterCore.FlatpakBackend : Object { // Clear any packages previously marked as updatable for (int i = (int) n_updatable_packages - 1; i >= 0; i--) { var package = (Package) updatable_packages.get_item (i); - package.change_information.clear_update_info (); - package.update_state (); + package.update_information = null; } var job = yield launch_job (Job.Type.GET_UPDATES, job_args); + var runtime_update_information = new UpdateInformation (); + foreach (var update in (Gee.ArrayList)job.result.get_object ()) { - var package = package_list[update] ?? runtime_updates; + if (!package_list.has_key (update)) { + runtime_update_information.updatable_packages.add (update); + continue; + } + + var package = package_list[update]; + var update_info = new UpdateInformation (); - package.change_information.updatable_packages.add (update); + update_info.updatable_packages.add (update); try { - package.change_information.size += yield get_download_size (package, cancellable, true); + update_info.size = yield get_download_size (package, cancellable, true); } catch (Error e) { warning ("Error getting download size for package %s: %s", update, e.message); } - package.update_state (); + package.update_information = update_info; } - fill_runtime_updates (); + fill_runtime_updates (runtime_update_information); } private void repair_internal (Job job) { diff --git a/src/Core/Job.vala b/src/Core/Job.vala index 29a04d1d8..f4c2bd42f 100644 --- a/src/Core/Job.vala +++ b/src/Core/Job.vala @@ -88,20 +88,17 @@ public class AppCenterCore.GetInstalledPackagesArgs : JobArgs { public class AppCenterCore.InstallPackageArgs : JobArgs { public Package package; - public ChangeInformation? change_info; - public Cancellable? cancellable; + public ChangeInformation change_info; } public class AppCenterCore.UpdatePackageArgs : JobArgs { public Package package; - public ChangeInformation? change_info; - public Cancellable? cancellable; + public ChangeInformation change_info; } public class AppCenterCore.RemovePackageArgs : JobArgs { public Package package; - public ChangeInformation? change_info; - public Cancellable? cancellable; + public ChangeInformation change_info; } public class AppCenterCore.GetDownloadSizeArgs : JobArgs { diff --git a/src/Core/Package.vala b/src/Core/Package.vala index 05cfbd84e..9c77ec6b2 100644 --- a/src/Core/Package.vala +++ b/src/Core/Package.vala @@ -60,13 +60,6 @@ public class AppCenterCore.Package : Object { "language-discrimination" }; - public signal void changing (bool is_changing); - /** - * This signal is likely to be fired from a non-main thread. Ensure any UI - * logic driven from this runs on the GTK thread - */ - public signal void info_changed (ChangeInformation.Status status); - public enum State { NOT_INSTALLED, INSTALLED, @@ -107,12 +100,14 @@ public class AppCenterCore.Package : Object { public AppStream.Component component { get; protected set; } public ChangeInformation change_information { public get; private set; } - public GLib.Cancellable action_cancellable { public get; private set; } public State state { public get; private set; default = State.NOT_INSTALLED; } - public double progress { - get { - return change_information.progress; + private UpdateInformation? _update_information; + public UpdateInformation? update_information { + get { return _update_information; } + set { + _update_information = value; + update_state (); } } @@ -193,12 +188,6 @@ public class AppCenterCore.Package : Object { } } - public bool changes_finished { - get { - return change_information.status == ChangeInformation.Status.FINISHED; - } - } - public bool is_runtime_updates { get { return component.id == RUNTIME_UPDATES_ID; @@ -424,9 +413,6 @@ public class AppCenterCore.Package : Object { construct { change_information = new ChangeInformation (); - change_information.status_changed.connect (() => info_changed (change_information.status)); - - action_cancellable = new GLib.Cancellable (); } public Package (string uid, AppStream.Component component) { @@ -454,7 +440,7 @@ public class AppCenterCore.Package : Object { State new_state; if (installed) { - if (change_information.has_changes ()) { + if (update_information != null) { new_state = State.UPDATE_AVAILABLE; } else { new_state = State.INSTALLED; @@ -546,9 +532,6 @@ public class AppCenterCore.Package : Object { } private void prepare_package_operation (State initial_state) { - changing (true); - - action_cancellable.reset (); change_information.start (); state = initial_state; @@ -560,20 +543,19 @@ public class AppCenterCore.Package : Object { switch (state) { case State.UPDATING: - var success = yield backend.update_package (this, change_information, action_cancellable); + var success = yield backend.update_package (this, change_information); if (success) { - change_information.clear_update_info (); - update_state (); + update_information = null; } return success; case State.INSTALLING: - var success = yield backend.install_package (this, change_information, action_cancellable); + var success = yield backend.install_package (this, change_information); _installed = success; update_state (); return success; case State.REMOVING: - var success = yield backend.remove_package (this, change_information, action_cancellable); + var success = yield backend.remove_package (this, change_information); _installed = !success; update_state (); return success; @@ -583,14 +565,12 @@ public class AppCenterCore.Package : Object { } private void clean_up_package_operation (bool success, State success_state, State fail_state) { - changing (false); + change_information.complete (); if (success) { - change_information.complete (); state = success_state; } else { state = fail_state; - change_information.cancel (); } FlatpakBackend.get_default ().notify_package_changed (this); @@ -665,10 +645,6 @@ public class AppCenterCore.Package : Object { summary = new_summary; } - public string get_progress_description () { - return change_information.status_description; - } - public GLib.Icon get_icon (uint size, uint scale_factor) { GLib.Icon? icon = null; uint current_size = 0; diff --git a/src/Core/UpdateInformation.vala b/src/Core/UpdateInformation.vala new file mode 100644 index 000000000..e3d4b6808 --- /dev/null +++ b/src/Core/UpdateInformation.vala @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +public class AppCenterCore.UpdateInformation : Object { + public Gee.ArrayList updatable_packages { get; private set; } + public uint64 size { get; set; } + + construct { + updatable_packages = new Gee.ArrayList (); + size = 0; + } +} diff --git a/src/Widgets/ActionStack.vala b/src/Widgets/ActionStack.vala index 703392cd8..613bc4915 100644 --- a/src/Widgets/ActionStack.vala +++ b/src/Widgets/ActionStack.vala @@ -78,7 +78,6 @@ public class AppCenter.ActionStack : Gtk.Box { cancel_button = new ProgressButton (package) { valign = CENTER }; - cancel_button.clicked.connect (() => action_cancelled ()); var action_button_group = new Gtk.SizeGroup (Gtk.SizeGroupMode.BOTH); action_button_group.add_widget (action_button); @@ -178,11 +177,6 @@ public class AppCenter.ActionStack : Gtk.Box { } } - private void action_cancelled () { - update_action (); - package.action_cancellable.cancel (); - } - private void launch_package_app () { try { package.launch (); diff --git a/src/Widgets/ProgressButton.vala b/src/Widgets/ProgressButton.vala index a3036f0eb..15a4138bf 100644 --- a/src/Widgets/ProgressButton.vala +++ b/src/Widgets/ProgressButton.vala @@ -6,8 +6,6 @@ public class AppCenter.ProgressButton : Gtk.Button { public AppCenterCore.Package package { get; construct; } - private Gtk.ProgressBar progressbar; - public ProgressButton (AppCenterCore.Package package) { Object (package: package); } @@ -16,42 +14,22 @@ public class AppCenter.ProgressButton : Gtk.Button { add_css_class ("progress"); add_css_class ("text-button"); - package.change_information.progress_changed.connect (update_progress); - package.change_information.status_changed.connect (update_progress_status); - - update_progress_status (); - update_progress (); - var cancel_label = new Gtk.Label (_("Cancel")) { mnemonic_widget = this }; - progressbar = new Gtk.ProgressBar (); + var progressbar = new Gtk.ProgressBar (); + package.change_information.bind_property ("progress", progressbar, "fraction", SYNC_CREATE); var box = new Gtk.Box (VERTICAL, 0); box.append (cancel_label); box.append (progressbar); child = box; - } - private void update_progress () { - Idle.add (() => { - progressbar.fraction = package.progress; - return GLib.Source.REMOVE; - }); - } + package.change_information.bind_property ("can-cancel", this, "sensitive", SYNC_CREATE); + package.change_information.bind_property ("status-description", this, "tooltip-text", SYNC_CREATE); - private void update_progress_status () { - Idle.add (() => { - tooltip_text = package.get_progress_description (); - sensitive = package.change_information.can_cancel && !package.changes_finished; - /* Ensure progress bar shows complete to match status (lp:1606902) */ - if (package.changes_finished) { - progressbar.fraction = 1.0f; - } - - return GLib.Source.REMOVE; - }); + clicked.connect (package.change_information.cancel); } } diff --git a/src/meson.build b/src/meson.build index 1008c3939..21bf7e7d4 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,6 +18,7 @@ appcenter_files = files( 'Core/SearchEngine.vala', 'Core/SoupClient.vala', 'Core/Stripe.vala', + 'Core' / 'UpdateInformation.vala', 'Core/UpdateManager.vala', 'Dialogs/InstallFailDialog.vala', 'Dialogs' / 'ReleasesDialog.vala',