Files
Bottles/bottles/frontend/component_entry_row.py
2025-01-16 19:45:23 -05:00

191 lines
6.2 KiB
Python

# component_entry_row.py
#
# Copyright 2025 The Bottles Contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, in version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from gettext import gettext as _
from gi.repository import Gtk, GObject, Adw
from bottles.backend.logger import Logger
from bottles.backend.state import Status
from bottles.backend.utils.manager import ManagerUtils
from bottles.backend.utils.threading import RunAsync
from bottles.frontend.gtk import GtkUtils
logging = Logger()
@Gtk.Template(resource_path="/com/usebottles/bottles/component-entry-row.ui")
class ComponentEntryRow(Adw.ActionRow):
__gtype_name__ = "ComponentEntryRow"
__gsignals__ = {
"component-installed": (GObject.SIGNAL_RUN_FIRST, None, ()),
"component-error": (GObject.SIGNAL_RUN_FIRST, None, ()),
}
# region Widgets
btn_download = Gtk.Template.Child()
btn_browse = Gtk.Template.Child()
btn_remove = Gtk.Template.Child()
btn_err = Gtk.Template.Child()
btn_cancel = Gtk.Template.Child()
box_download_status = Gtk.Template.Child()
label_task_status = Gtk.Template.Child()
# endregion
def __init__(
self, window, component, component_type, is_upgradable=False, **kwargs
):
super().__init__(**kwargs)
# common variables and references
self.window = window
self.manager = window.manager
self.component_manager = self.manager.component_manager
self.name = component[0]
self.component_type = component_type
self.is_upgradable = is_upgradable
# populate widgets
self.set_title(self.name)
self.set_can_focus(False)
if component[1].get("Installed"):
self.btn_browse.set_visible(True)
if not self.manager.component_manager.is_in_use(
self.component_type, self.name
):
self.btn_remove.set_visible(True)
else:
self.btn_download.set_visible(True)
self.btn_browse.set_visible(False)
if is_upgradable:
self.btn_download.set_icon_name("software-update-available-symbolic")
self.btn_download.set_tooltip_text(_("Upgrade"))
# connect signals
self.btn_download.connect("clicked", self.download)
self.btn_err.connect("clicked", self.download)
self.btn_remove.connect("clicked", self.uninstall)
self.btn_browse.connect("clicked", self.run_browse)
def download(self, widget):
@GtkUtils.run_in_main_loop
def async_callback(result, error=False):
if not result or result.status:
return self.set_installed()
return self.update_progress(status=Status.FAILED)
@GtkUtils.run_in_main_loop
def async_func(*args, **kwargs):
return self.update_progress(*args, **kwargs)
self.btn_download.set_visible(False)
self.btn_cancel.set_visible(False) # TODO: unimplemented
self.box_download_status.set_visible(True)
RunAsync(
task_func=self.component_manager.install,
callback=async_callback,
component_type=self.component_type,
component_name=self.name,
func=async_func,
)
def uninstall(self, widget):
@GtkUtils.run_in_main_loop
def update(result, error=False):
if result.ok:
return self.set_uninstalled()
return self.set_err(result.data.get("message"), retry=False)
self.btn_err.set_visible(False)
self.btn_remove.set_visible(False)
RunAsync(
task_func=self.component_manager.uninstall,
callback=update,
component_type=self.component_type,
component_name=self.name,
)
def run_browse(self, widget):
self.btn_download.set_visible(False)
ManagerUtils.open_filemanager(
path_type=self.component_type, component=self.name
)
def update_progress(
self,
received_size: int = 0,
total_size: int = 0,
status: Status | None = None,
):
if status == Status.FAILED:
logging.error("Component installation failed")
self.set_err()
return False
self.box_download_status.set_visible(True)
percent = int(received_size * 100 / total_size)
self.label_task_status.set_text(f"{percent}%")
if percent >= 100:
self.label_task_status.set_text(_("Installing…"))
def set_err(self, msg=None, retry=True):
self.box_download_status.set_visible(False)
self.btn_remove.set_visible(False)
self.btn_cancel.set_visible(False)
self.btn_browse.set_visible(False)
self.btn_err.set_visible(True)
if msg:
self.btn_err.set_tooltip_text(msg)
if not retry:
self.btn_err.set_sensitive(False)
def set_installed(self):
self.btn_err.set_visible(False)
self.box_download_status.set_visible(False)
self.btn_browse.set_visible(True)
self.btn_cancel.set_visible(False)
if not self.manager.component_manager.is_in_use(self.component_type, self.name):
self.btn_remove.set_visible(True)
def set_uninstalled(self):
self.btn_browse.set_visible(False)
self.btn_err.set_visible(False)
self.btn_download.set_visible(True)
if self.name in self.manager.get_offline_components(
self.component_type, self.name
):
self.set_visible(False)
class ComponentExpander(Adw.ExpanderRow):
def __init__(self, title, subtitle=None, **kwargs):
super().__init__(**kwargs)
self.set_title(title)
if subtitle:
self.set_subtitle(subtitle)