diff --git a/src/change_version_page/change_version_page.blp b/src/change_version_page/change_version_page.blp index af31adf..1daa7dc 100644 --- a/src/change_version_page/change_version_page.blp +++ b/src/change_version_page/change_version_page.blp @@ -2,59 +2,59 @@ using Gtk 4.0; using Adw 1; template $ChangeVersionPage : Adw.NavigationPage { - title: _("Change Versions"); - Adw.ToolbarView { - [top] - Adw.HeaderBar { - } - Adw.ToastOverlay toast_overlay { - ScrolledWindow scrolled_window {} - } - [bottom] - ActionBar action_bar { - revealed: false; - [center] - Button apply_button { - sensitive: bind action_bar.revealed; - halign: center; - margin-top: 3; - margin-bottom: 3; - Adw.ButtonContent { - label: _("Change Version"); - icon-name: "double-ended-arrows-vertical-symbolic"; - } - styles ["suggested-action", "pill"] - } - } - } + title: _("Change Versions"); + Adw.ToolbarView { + [top] + Adw.HeaderBar { + } + Adw.ToastOverlay toast_overlay { + ScrolledWindow scrolled_window {} + } + [bottom] + ActionBar action_bar { + revealed: false; + [center] + Button apply_button { + sensitive: bind action_bar.revealed; + halign: center; + margin-top: 3; + margin-bottom: 3; + Adw.ButtonContent { + label: _("Change Version"); + icon-name: "double-ended-arrows-vertical-symbolic"; + } + styles ["suggested-action", "pill"] + } + } + } } Adw.Clamp versions_clamp { - Box { - margin-start: 12; - margin-end: 12; - margin-top: 12; - margin-bottom: 12; - spacing: 12; - orientation: vertical; - halign: fill; - hexpand: true; + Box { + margin-start: 12; + margin-end: 12; + margin-top: 12; + margin-bottom: 12; + spacing: 12; + orientation: vertical; + halign: fill; + hexpand: true; - CheckButton root_group_check_button { - visible: false; - active: true; - } + CheckButton root_group_check_button { + visible: false; + active: true; + } - Adw.PreferencesGroup mask_group { - Adw.SwitchRow mask_row { - title: _("Disable Updates"); - active: true; - } - } + Adw.PreferencesGroup mask_group { + Adw.SwitchRow mask_row { + title: _("Disable Updates"); + active: true; + } + } - Adw.PreferencesGroup versions_group { - title: _("Select a Release"); - description: _("This will uninstall the current release and install the chosen one instead. Note that downgrading can cause issues."); - } - } + Adw.PreferencesGroup versions_group { + title: _("Select a Release"); + description: _("This will uninstall the current release and install the chosen one instead. Note that downgrading can cause issues."); + } + } } diff --git a/src/gtk/app_row.blp b/src/gtk/app_row.blp index 9b3ab3b..e4dc980 100644 --- a/src/gtk/app_row.blp +++ b/src/gtk/app_row.blp @@ -2,42 +2,42 @@ using Gtk 4.0; using Adw 1; template $AppRow : Adw.ActionRow { - activatable: true; - [prefix] - Image image { - icon-size: large; - icon-name: "application-x-executable-symbolic"; - } - [suffix] - Image eol_package_package_status_icon { - icon-name: "error-symbolic"; - tooltip-text: _("This package is End Of Life, and will not receive any security updates"); - visible: false; - styles["error"] - } - [suffix] - Image eol_runtime_status_icon { - icon-name: "error-symbolic"; - tooltip-text: _("This app's runtime is End Of Life, and will not receive any security updates"); - visible: false; - styles["error"] - } - [suffix] - Image pinned_status_icon { - icon-name: "pin-symbolic"; - tooltip-text: _("This runtime will never be automatically removed"); - visible: false; - } - [suffix] - Image masked_status_icon { - icon-name: "software-update-urgent-symbolic"; - tooltip-text: _("Updates are disabled for this package"); - visible: false; - } - [suffix] - CheckButton check_button { - margin-start: 6; - styles["selection-mode"] - visible: false; - } + activatable: true; + [prefix] + Image image { + icon-size: large; + icon-name: "application-x-executable-symbolic"; + } + [suffix] + Image eol_package_package_status_icon { + icon-name: "error-symbolic"; + tooltip-text: _("This package is End Of Life, and will not receive any security updates"); + visible: false; + styles["error"] + } + [suffix] + Image eol_runtime_status_icon { + icon-name: "error-symbolic"; + tooltip-text: _("This app's runtime is End Of Life, and will not receive any security updates"); + visible: false; + styles["error"] + } + [suffix] + Image pinned_status_icon { + icon-name: "pin-symbolic"; + tooltip-text: _("This runtime will never be automatically removed"); + visible: false; + } + [suffix] + Image masked_status_icon { + icon-name: "software-update-urgent-symbolic"; + tooltip-text: _("Updates are disabled for this package"); + visible: false; + } + [suffix] + CheckButton check_button { + margin-start: 6; + styles["selection-mode"] + visible: false; + } } diff --git a/src/gtk/attempt_install_dialog.blp b/src/gtk/attempt_install_dialog.blp index 27baf36..69cca66 100644 --- a/src/gtk/attempt_install_dialog.blp +++ b/src/gtk/attempt_install_dialog.blp @@ -2,7 +2,7 @@ using Gtk 4.0; using Adw 1; template $AttemptInstallDialog : Adw.AlertDialog { - heading: _("Attempt an Install?"); + heading: _("Attempt an Install?"); body: _("Warehouse will try to install the matching packages."); responses [ cancel: _("Cancel"), diff --git a/src/gtk/loading_status.blp b/src/gtk/loading_status.blp index a9c397a..14f02d6 100644 --- a/src/gtk/loading_status.blp +++ b/src/gtk/loading_status.blp @@ -2,56 +2,56 @@ using Gtk 4.0; using Adw 1; template $LoadingStatus : ScrolledWindow { - Box { - orientation: vertical; - valign: center; - halign: fill; - spacing: 12; - margin-start: 12; - margin-end: 12; - margin-top: 12; - margin-bottom: 12; - Adw.Spinner { - height-request: 30; - margin-bottom: 12; - opacity: 0.5; - } - Label title_label { - label: "No Title Set"; - wrap: true; - justify: center; - styles ["title-1"] - } - Label description_label { - label: "No Description Set"; - wrap: true; - justify: center; - styles ["description", "body"] - } - Adw.Clamp progress_clamp { - margin-start: 24; - margin-end: 24; - margin-top: 12; - margin-bottom: 12; - maximum-size: 400; - Box { - halign: fill; - hexpand: true; - spacing: 12; - ProgressBar progress_bar { - halign: fill; - hexpand: true; - valign: center; - } - Label progress_label { - valign: center; - } - } - } - Button button { - label: _("Cancel"); - styles ["pill"] - halign: center; - } - } + Box { + orientation: vertical; + valign: center; + halign: fill; + spacing: 12; + margin-start: 12; + margin-end: 12; + margin-top: 12; + margin-bottom: 12; + Adw.Spinner { + height-request: 30; + margin-bottom: 12; + opacity: 0.5; + } + Label title_label { + label: "No Title Set"; + wrap: true; + justify: center; + styles ["title-1"] + } + Label description_label { + label: "No Description Set"; + wrap: true; + justify: center; + styles ["description", "body"] + } + Adw.Clamp progress_clamp { + margin-start: 24; + margin-end: 24; + margin-top: 12; + margin-bottom: 12; + maximum-size: 400; + Box { + halign: fill; + hexpand: true; + spacing: 12; + ProgressBar progress_bar { + halign: fill; + hexpand: true; + valign: center; + } + Label progress_label { + valign: center; + } + } + } + Button button { + label: _("Cancel"); + styles ["pill"] + halign: center; + } + } } diff --git a/src/gtk/loading_status.py b/src/gtk/loading_status.py index 8f93472..2ac8a50 100644 --- a/src/gtk/loading_status.py +++ b/src/gtk/loading_status.py @@ -2,23 +2,23 @@ from gi.repository import Gtk, GLib @Gtk.Template(resource_path="/io/github/flattool/Warehouse/gtk/loading_status.ui") class LoadingStatus(Gtk.ScrolledWindow): - __gtype_name__ = 'LoadingStatus' - gtc = Gtk.Template.Child + __gtype_name__ = 'LoadingStatus' + gtc = Gtk.Template.Child - title_label = gtc() - description_label = gtc() - progress_clamp = gtc() - progress_bar = gtc() - progress_label = gtc() - button = gtc() + title_label = gtc() + description_label = gtc() + progress_clamp = gtc() + progress_bar = gtc() + progress_label = gtc() + button = gtc() - def __init__(self, title, description, show_progress=False, on_cancel=None, **kwargs): - super().__init__(**kwargs) + def __init__(self, title, description, show_progress=False, on_cancel=None, **kwargs): + super().__init__(**kwargs) - self.title_label.set_label(GLib.markup_escape_text(title)) - self.description_label.set_label(GLib.markup_escape_text(description)) - self.progress_clamp.set_visible(show_progress) - if on_cancel is None: - self.button.set_visible(False) - else: - self.button.connect("clicked", lambda *_: on_cancel()) + self.title_label.set_label(GLib.markup_escape_text(title)) + self.description_label.set_label(GLib.markup_escape_text(description)) + self.progress_clamp.set_visible(show_progress) + if on_cancel is None: + self.button.set_visible(False) + else: + self.button.connect("clicked", lambda *_: on_cancel()) diff --git a/src/gtk/sidebar_button.py b/src/gtk/sidebar_button.py index 7e12753..f21469d 100644 --- a/src/gtk/sidebar_button.py +++ b/src/gtk/sidebar_button.py @@ -2,18 +2,18 @@ from gi.repository import Gtk from .host_info import HostInfo class SidebarButton(Gtk.Button): - __gtype_name__ = "SidebarButton" + __gtype_name__ = "SidebarButton" - def __init__(self, **kwargs): - super().__init__(**kwargs) - - # Extra Object Creation - main_split = HostInfo.main_window.main_split + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # Extra Object Creation + main_split = HostInfo.main_window.main_split - # Connections - main_split.connect("notify::collapsed", lambda *_: self.set_visible(main_split.get_collapsed())) - self.connect("clicked", lambda *_: main_split.set_show_sidebar(True)) + # Connections + main_split.connect("notify::collapsed", lambda *_: self.set_visible(main_split.get_collapsed())) + self.connect("clicked", lambda *_: main_split.set_show_sidebar(True)) - # Apply - self.set_icon_name("dock-left-symbolic") - self.set_tooltip_text(_("Show Sidebar")) + # Apply + self.set_icon_name("dock-left-symbolic") + self.set_tooltip_text(_("Show Sidebar")) diff --git a/src/install_page/file_install_dialog.blp b/src/install_page/file_install_dialog.blp index 847b496..f7c824c 100644 --- a/src/install_page/file_install_dialog.blp +++ b/src/install_page/file_install_dialog.blp @@ -2,40 +2,40 @@ using Gtk 4.0; using Adw 1; template $FileInstallDialog : Adw.Dialog { - follows-content-size: true; - Adw.ToolbarView { - [top] - Adw.HeaderBar { - show-start-title-buttons: false; - show-end-title-buttons: false; - [start] - Button cancel_button { - label: _("Cancel"); - } - [end] - Button apply_button { - styles ["suggested-action"] - label: _("Install"); - } - } - ScrolledWindow content_page { - propagate-natural-height: true; - propagate-natural-width: true; - Adw.Clamp { - margin-start: 12; - margin-end: 12; - margin-bottom: 12; - margin-top: 6; - Box { - orientation: vertical; - spacing: 12; - Adw.PreferencesGroup packages_group { - title: _("Review Selection"); - } - $InstallationChooser installation_chooser { - } - } - } - } - } + follows-content-size: true; + Adw.ToolbarView { + [top] + Adw.HeaderBar { + show-start-title-buttons: false; + show-end-title-buttons: false; + [start] + Button cancel_button { + label: _("Cancel"); + } + [end] + Button apply_button { + styles ["suggested-action"] + label: _("Install"); + } + } + ScrolledWindow content_page { + propagate-natural-height: true; + propagate-natural-width: true; + Adw.Clamp { + margin-start: 12; + margin-end: 12; + margin-bottom: 12; + margin-top: 6; + Box { + orientation: vertical; + spacing: 12; + Adw.PreferencesGroup packages_group { + title: _("Review Selection"); + } + $InstallationChooser installation_chooser { + } + } + } + } + } } diff --git a/src/install_page/install_page.blp b/src/install_page/install_page.blp index a1a1e51..c3b238b 100644 --- a/src/install_page/install_page.blp +++ b/src/install_page/install_page.blp @@ -2,106 +2,106 @@ using Gtk 4.0; using Adw 1; template $InstallPage : Adw.BreakpointBin { - width-request: 1; + width-request: 1; height-request: 1; - Adw.Breakpoint break_point { + Adw.Breakpoint break_point { condition ("max-width: 600") setters { - multi_view.layout: skinny; + multi_view.layout: skinny; } } - Adw.NavigationPage { - title: _("Install Packages"); - Adw.ToastOverlay toast_overlay { - Stack status_stack { - Adw.ToolbarView loading_view { - [top] - Adw.HeaderBar { - [start] - $SidebarButton {} - } - } - Adw.ToolbarView installing_view { - [top] - Adw.HeaderBar { - [start] - $SidebarButton {} - } - } - Adw.MultiLayoutView multi_view { - Adw.Layout wide { - Adw.NavigationSplitView split_view { - sidebar-width-fraction: 0.5; - max-sidebar-width: 999999999; - sidebar: - Adw.NavigationPage { - title: _("Select Source"); - Adw.LayoutSlot { - id: "select_page"; - } - } - ; - content: - Adw.NavigationPage { - title: _("Pending Packages"); - Adw.LayoutSlot { - id: "pending_page"; - } - } - ; - } - } - Adw.Layout skinny { - Adw.BottomSheet bottom_sheet { - [content] - Box { - margin-bottom: bind bottom_sheet.bottom-bar-height; - Adw.LayoutSlot { - id: "select_page"; - } - } - [sheet] - Adw.LayoutSlot { - id: "pending_page"; - } - } - } - [select_page] - $SelectPage select_page {} - [pending_page] - $PendingPage pending_page {} - } - } - } - } + Adw.NavigationPage { + title: _("Install Packages"); + Adw.ToastOverlay toast_overlay { + Stack status_stack { + Adw.ToolbarView loading_view { + [top] + Adw.HeaderBar { + [start] + $SidebarButton {} + } + } + Adw.ToolbarView installing_view { + [top] + Adw.HeaderBar { + [start] + $SidebarButton {} + } + } + Adw.MultiLayoutView multi_view { + Adw.Layout wide { + Adw.NavigationSplitView split_view { + sidebar-width-fraction: 0.5; + max-sidebar-width: 999999999; + sidebar: + Adw.NavigationPage { + title: _("Select Source"); + Adw.LayoutSlot { + id: "select_page"; + } + } + ; + content: + Adw.NavigationPage { + title: _("Pending Packages"); + Adw.LayoutSlot { + id: "pending_page"; + } + } + ; + } + } + Adw.Layout skinny { + Adw.BottomSheet bottom_sheet { + [content] + Box { + margin-bottom: bind bottom_sheet.bottom-bar-height; + Adw.LayoutSlot { + id: "select_page"; + } + } + [sheet] + Adw.LayoutSlot { + id: "pending_page"; + } + } + } + [select_page] + $SelectPage select_page {} + [pending_page] + $PendingPage pending_page {} + } + } + } + } } Revealer bottom_child { - reveal-child: false; - Box { - margin-top: 12; - margin-bottom: 14; - spacing: 12; - halign: center; - valign: center; - styles ["flat"] - [start] - Image { - icon-name: "flatpak-symbolic"; - icon-size: normal; - } - [center] - Label bottom_label { - label: _("Pending Packages"); - styles ["heading"] - } - [end] - Image { - icon-name: "right-large-symbolic"; - icon-size: normal; - } - } + reveal-child: false; + Box { + margin-top: 12; + margin-bottom: 14; + spacing: 12; + halign: center; + valign: center; + styles ["flat"] + [start] + Image { + icon-name: "flatpak-symbolic"; + icon-size: normal; + } + [center] + Label bottom_label { + label: _("Pending Packages"); + styles ["heading"] + } + [end] + Image { + icon-name: "right-large-symbolic"; + icon-size: normal; + } + } } diff --git a/src/install_page/install_page.py b/src/install_page/install_page.py index 9e004fb..e7f73ba 100644 --- a/src/install_page/install_page.py +++ b/src/install_page/install_page.py @@ -9,89 +9,89 @@ from .error_toast import ErrorToast @Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/install_page.ui") class InstallPage(Adw.BreakpointBin): - __gtype_name__ = "InstallPage" - gtc = Gtk.Template.Child - - break_point = gtc() - split_view = gtc() - multi_view = gtc() - select_page = gtc() - pending_page = gtc() - status_stack = gtc() - loading_view = gtc() - installing_view = gtc() - bottom_sheet = gtc() - bottom_child = gtc() - bottom_label = gtc() - - # Referred to in the main window - # It is used to determine if a new page should be made or not - # This must be set to the created object from within the class's __init__ method - instance = None - page_name = "install" - - current_installation = "" - current_remote = None - did_error = False - - def start_loading(self): - self.total_added_packages = 0 - self.bottom_bar_visual_handler(False) - self.status_stack.set_visible_child(self.loading_view) - self.select_page.start_loading() - self.pending_page.reset() - - def end_loading(self): - self.select_page.end_loading() - self.status_stack.set_visible_child(self.multi_view) - - def install_callback(self): - HostInfo.main_window.refresh_handler() - if not self.did_error: - HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Installed Packages"))) - - def install_error_callback(self, user_facing_label, error_message): - self.did_error = True - GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast)) - - def install_packages(self, package_requests): - self.did_error = False - if PackageInstallWorker.install(package_requests, self.installing_status, self.install_callback, self.install_error_callback): - self.status_stack.set_visible_child(self.installing_view) - - def bottom_bar_visual_handler(self, is_added): - total = self.total_added_packages - if total == 0: - self.bottom_child.set_reveal_child(False) - self.bottom_sheet.set_bottom_bar(None) - elif total == 1: - self.bottom_label.set_label(_("{} Pending Package").format(1)) - if is_added: - self.bottom_sheet.set_bottom_bar(self.bottom_child) - self.bottom_child.set_reveal_child(True) - else: - self.bottom_label.set_label(_("{} Pending Packages").format(total)) - - def package_added(self): - self.total_added_packages += 1 - self.bottom_bar_visual_handler(True) - - def package_removed(self): - self.total_added_packages -= 1 - self.bottom_bar_visual_handler(False) - - def __init__(self, main_window, **kwargs): - super().__init__(**kwargs) - self.instance = self - - # Extra Object Creation - self.installing_status = LoadingStatus(_("Installing Packages"), _("This could take a while"), True, PackageInstallWorker.cancel) - self.total_added_packages = 0 - - # Connections - - # Apply - self.select_page.results_page.pending_page = self.pending_page - self.select_page.results_page.install_page = self - self.loading_view.set_content(LoadingStatus(_("Loading Installation Options"), _("This should only take a moment"))) - self.installing_view.set_content(self.installing_status) + __gtype_name__ = "InstallPage" + gtc = Gtk.Template.Child + + break_point = gtc() + split_view = gtc() + multi_view = gtc() + select_page = gtc() + pending_page = gtc() + status_stack = gtc() + loading_view = gtc() + installing_view = gtc() + bottom_sheet = gtc() + bottom_child = gtc() + bottom_label = gtc() + + # Referred to in the main window + # It is used to determine if a new page should be made or not + # This must be set to the created object from within the class's __init__ method + instance = None + page_name = "install" + + current_installation = "" + current_remote = None + did_error = False + + def start_loading(self): + self.total_added_packages = 0 + self.bottom_bar_visual_handler(False) + self.status_stack.set_visible_child(self.loading_view) + self.select_page.start_loading() + self.pending_page.reset() + + def end_loading(self): + self.select_page.end_loading() + self.status_stack.set_visible_child(self.multi_view) + + def install_callback(self): + HostInfo.main_window.refresh_handler() + if not self.did_error: + HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Installed Packages"))) + + def install_error_callback(self, user_facing_label, error_message): + self.did_error = True + GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast)) + + def install_packages(self, package_requests): + self.did_error = False + if PackageInstallWorker.install(package_requests, self.installing_status, self.install_callback, self.install_error_callback): + self.status_stack.set_visible_child(self.installing_view) + + def bottom_bar_visual_handler(self, is_added): + total = self.total_added_packages + if total == 0: + self.bottom_child.set_reveal_child(False) + self.bottom_sheet.set_bottom_bar(None) + elif total == 1: + self.bottom_label.set_label(_("{} Pending Package").format(1)) + if is_added: + self.bottom_sheet.set_bottom_bar(self.bottom_child) + self.bottom_child.set_reveal_child(True) + else: + self.bottom_label.set_label(_("{} Pending Packages").format(total)) + + def package_added(self): + self.total_added_packages += 1 + self.bottom_bar_visual_handler(True) + + def package_removed(self): + self.total_added_packages -= 1 + self.bottom_bar_visual_handler(False) + + def __init__(self, main_window, **kwargs): + super().__init__(**kwargs) + self.instance = self + + # Extra Object Creation + self.installing_status = LoadingStatus(_("Installing Packages"), _("This could take a while"), True, PackageInstallWorker.cancel) + self.total_added_packages = 0 + + # Connections + + # Apply + self.select_page.results_page.pending_page = self.pending_page + self.select_page.results_page.install_page = self + self.loading_view.set_content(LoadingStatus(_("Loading Installation Options"), _("This should only take a moment"))) + self.installing_view.set_content(self.installing_status) diff --git a/src/install_page/pending_page.blp b/src/install_page/pending_page.blp index 2c0aae5..1a357c7 100644 --- a/src/install_page/pending_page.blp +++ b/src/install_page/pending_page.blp @@ -2,40 +2,40 @@ using Gtk 4.0; using Adw 1; template $PendingPage : Adw.NavigationPage { - title: _("Pending Packages"); - Stack stack { - Adw.ToolbarView none_pending { - [top] - Adw.HeaderBar { - } - Adw.StatusPage { - icon-name: "flatpak-symbolic"; - title: _("Add Packages"); - description: _("Packages queued to install will show up here"); - } - } - Adw.ToolbarView main_view { - [top] - Adw.HeaderBar { - } - Adw.PreferencesPage preferences_page { - } - [bottom] - ActionBar pending_action_bar { - revealed: true; - [center] - Button install_button { - margin-top: 3; - margin-bottom: 3; - sensitive: bind pending_action_bar.revealed; - styles ["pill", "suggested-action"] - Adw.ButtonContent { - can-shrink: true; - icon-name: "arrow-pointing-at-line-down-symbolic"; - label: _("Install"); - } - } - } - } - } + title: _("Pending Packages"); + Stack stack { + Adw.ToolbarView none_pending { + [top] + Adw.HeaderBar { + } + Adw.StatusPage { + icon-name: "flatpak-symbolic"; + title: _("Add Packages"); + description: _("Packages queued to install will show up here"); + } + } + Adw.ToolbarView main_view { + [top] + Adw.HeaderBar { + } + Adw.PreferencesPage preferences_page { + } + [bottom] + ActionBar pending_action_bar { + revealed: true; + [center] + Button install_button { + margin-top: 3; + margin-bottom: 3; + sensitive: bind pending_action_bar.revealed; + styles ["pill", "suggested-action"] + Adw.ButtonContent { + can-shrink: true; + icon-name: "arrow-pointing-at-line-down-symbolic"; + label: _("Install"); + } + } + } + } + } } diff --git a/src/install_page/pending_page.py b/src/install_page/pending_page.py index 3e3710e..029455c 100644 --- a/src/install_page/pending_page.py +++ b/src/install_page/pending_page.py @@ -3,126 +3,126 @@ from .host_info import HostInfo from .result_row import ResultRow class AddedGroup(Adw.PreferencesGroup): - __gtype_name__ = "AddedGroup" - - def add_row(self, row): - self.rows.append(row) - self.add(row) - - def rem_row(self, row): - if row in self.rows: - self.rows.remove(row) - self.remove(row) - - def remove_all(self, *args): - while len(self.rows) > 0 and (row := self.rows[0]): - row.activate() - - def __init__(self, remote, installation, **kwargs): - super().__init__(**kwargs) - - self.remote = remote - self.installation = installation - self.rows = [] - - self.set_title(f"{remote.title}") - self.set_description(_("Installation: {}").format(installation)) - - remove_all = Gtk.Button( - child=Adw.ButtonContent( - icon_name="list-remove-all-symbolic", - label=_("Remove All"), - ), - valign = Gtk.Align.CENTER, - ) - remove_all.add_css_class("flat") - remove_all.connect("clicked", self.remove_all) - self.set_header_suffix(remove_all) - + __gtype_name__ = "AddedGroup" + + def add_row(self, row): + self.rows.append(row) + self.add(row) + + def rem_row(self, row): + if row in self.rows: + self.rows.remove(row) + self.remove(row) + + def remove_all(self, *args): + while len(self.rows) > 0 and (row := self.rows[0]): + row.activate() + + def __init__(self, remote, installation, **kwargs): + super().__init__(**kwargs) + + self.remote = remote + self.installation = installation + self.rows = [] + + self.set_title(f"{remote.title}") + self.set_description(_("Installation: {}").format(installation)) + + remove_all = Gtk.Button( + child=Adw.ButtonContent( + icon_name="list-remove-all-symbolic", + label=_("Remove All"), + ), + valign = Gtk.Align.CENTER, + ) + remove_all.add_css_class("flat") + remove_all.connect("clicked", self.remove_all) + self.set_header_suffix(remove_all) + @Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/pending_page.ui") class PendingPage(Adw.NavigationPage): - __gtype_name__ = "PendingPage" - gtc = Gtk.Template.Child - - stack = gtc() - main_view = gtc() - none_pending = gtc() - preferences_page = gtc() - install_button = gtc() - - def add_package_row(self, row): - self.added_packages.append(row.package) - row.set_state(ResultRow.PackageState.SELECTED) - key = f"{row.package.remote}<>{row.package.installation}" - added_row = ResultRow(row.package, ResultRow.PackageState.ADDED, row.origin_list_box) - group = None - try: - group = self.groups[key] - group.add_row(added_row) - except KeyError: - group = AddedGroup(added_row.package.remote, added_row.package.installation) - group.add_row(added_row) - self.groups[key] = group - self.preferences_page.add(group) - - added_row.connect("activated", self.remove_package_row, group) - self.stack.set_visible_child(self.main_view) - - def remove_package_row(self, row, group): - # row.origin_row.set_state(ResultRow.PackageState.NEW) - for item in row.origin_list_box: - if item.state == ResultRow.PackageState.SELECTED and item.package.is_similar(row.package): - item.set_state(ResultRow.PackageState.NEW) - break - - group.rem_row(row) - if row.package in self.added_packages: - self.added_packages.remove(row.package) - - if len(group.rows) == 0: - key = f"{row.package.remote}<>{row.package.installation}" - self.groups.pop(key, None) - self.preferences_page.remove(group) - - if len(self.added_packages) == 0: - self.stack.set_visible_child(self.none_pending) - - install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row] - install_page.package_removed() - - def on_install(self, *args): - package_requests = [] - for key, group in self.groups.items(): - item = { - "remote": group.remote.name, - "installation": group.installation, - "package_names": [], - "extra_flags": [], - } - for row in group.rows: - item['package_names'].append(row.package.app_id) - - package_requests.append(item) - - install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row] - install_page.install_packages(package_requests) - - def reset(self): - for key, group in self.groups.items(): - self.preferences_page.remove(group) - - self.groups.clear() - self.added_packages.clear() - self.stack.set_visible_child(self.none_pending) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - # Extra Object Creation - self.groups = {} # remote<>installation: adw.preference_group - self.added_packages = [] - - # Connections - self.install_button.connect("clicked", self.on_install) - - # Apply + __gtype_name__ = "PendingPage" + gtc = Gtk.Template.Child + + stack = gtc() + main_view = gtc() + none_pending = gtc() + preferences_page = gtc() + install_button = gtc() + + def add_package_row(self, row): + self.added_packages.append(row.package) + row.set_state(ResultRow.PackageState.SELECTED) + key = f"{row.package.remote}<>{row.package.installation}" + added_row = ResultRow(row.package, ResultRow.PackageState.ADDED, row.origin_list_box) + group = None + try: + group = self.groups[key] + group.add_row(added_row) + except KeyError: + group = AddedGroup(added_row.package.remote, added_row.package.installation) + group.add_row(added_row) + self.groups[key] = group + self.preferences_page.add(group) + + added_row.connect("activated", self.remove_package_row, group) + self.stack.set_visible_child(self.main_view) + + def remove_package_row(self, row, group): + # row.origin_row.set_state(ResultRow.PackageState.NEW) + for item in row.origin_list_box: + if item.state == ResultRow.PackageState.SELECTED and item.package.is_similar(row.package): + item.set_state(ResultRow.PackageState.NEW) + break + + group.rem_row(row) + if row.package in self.added_packages: + self.added_packages.remove(row.package) + + if len(group.rows) == 0: + key = f"{row.package.remote}<>{row.package.installation}" + self.groups.pop(key, None) + self.preferences_page.remove(group) + + if len(self.added_packages) == 0: + self.stack.set_visible_child(self.none_pending) + + install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row] + install_page.package_removed() + + def on_install(self, *args): + package_requests = [] + for key, group in self.groups.items(): + item = { + "remote": group.remote.name, + "installation": group.installation, + "package_names": [], + "extra_flags": [], + } + for row in group.rows: + item['package_names'].append(row.package.app_id) + + package_requests.append(item) + + install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row] + install_page.install_packages(package_requests) + + def reset(self): + for key, group in self.groups.items(): + self.preferences_page.remove(group) + + self.groups.clear() + self.added_packages.clear() + self.stack.set_visible_child(self.none_pending) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # Extra Object Creation + self.groups = {} # remote<>installation: adw.preference_group + self.added_packages = [] + + # Connections + self.install_button.connect("clicked", self.on_install) + + # Apply diff --git a/src/install_page/result_row.blp b/src/install_page/result_row.blp index d7928a2..ef09efe 100644 --- a/src/install_page/result_row.blp +++ b/src/install_page/result_row.blp @@ -2,48 +2,48 @@ using Gtk 4.0; using Adw 1; template $ResultRow : Adw.ActionRow { - activatable: true; - title: "No title set"; - subtitle: "No subtitle set"; - tooltip-text: _("Add Package to Queue"); + activatable: true; + title: "No title set"; + subtitle: "No subtitle set"; + tooltip-text: _("Add Package to Queue"); - Box { - orientation: vertical; - valign: center; - spacing: 4; - margin-end: 4; - Label version_label { - styles ["subtitle"] - label: ""; - justify: right; - halign: end; - hexpand: true; - wrap: true; - } - Label branch_label { - styles ["subtitle"] - label: ""; - justify: right; - halign: end; - hexpand: true; - wrap: true; - } - } - [suffix] - Image add_image { - icon-name: "plus-large-symbolic"; - } - [suffix] - Image selected_image { - icon-name: "check-plain-symbolic"; - } - [suffix] - Image sub_image { - icon-name: "minus-large-symbolic"; - } - [suffix] - Image installed_image { - icon-name: "selection-mode-symbolic"; - styles ["success"] - } + Box { + orientation: vertical; + valign: center; + spacing: 4; + margin-end: 4; + Label version_label { + styles ["subtitle"] + label: ""; + justify: right; + halign: end; + hexpand: true; + wrap: true; + } + Label branch_label { + styles ["subtitle"] + label: ""; + justify: right; + halign: end; + hexpand: true; + wrap: true; + } + } + [suffix] + Image add_image { + icon-name: "plus-large-symbolic"; + } + [suffix] + Image selected_image { + icon-name: "check-plain-symbolic"; + } + [suffix] + Image sub_image { + icon-name: "minus-large-symbolic"; + } + [suffix] + Image installed_image { + icon-name: "selection-mode-symbolic"; + styles ["success"] + } } diff --git a/src/install_page/result_row.py b/src/install_page/result_row.py index c5b630c..5061ec3 100644 --- a/src/install_page/result_row.py +++ b/src/install_page/result_row.py @@ -3,67 +3,67 @@ from enum import Enum @Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/result_row.ui") class ResultRow(Adw.ActionRow): - __gtype_name__ = "ResultRow" - gtc = Gtk.Template.Child + __gtype_name__ = "ResultRow" + gtc = Gtk.Template.Child - version_label = gtc() - branch_label = gtc() - add_image = gtc() - sub_image = gtc() - selected_image = gtc() - installed_image = gtc() + version_label = gtc() + branch_label = gtc() + add_image = gtc() + sub_image = gtc() + selected_image = gtc() + installed_image = gtc() - class PackageState(Enum): - NEW = 0 - SELECTED = 1 - ADDED = 2 - INSTALLED = 3 + class PackageState(Enum): + NEW = 0 + SELECTED = 1 + ADDED = 2 + INSTALLED = 3 - def idle_stuff(self): - self.set_title(GLib.markup_escape_text(self.package.name)) - self.set_subtitle(self.package.app_id) - self.version_label.set_label(GLib.markup_escape_text(self.package.version)) - self.branch_label.set_label(GLib.markup_escape_text(self.package.branch)) - self.version_label.set_visible(len(self.version_label.get_label()) != 0) - self.branch_label.set_visible(len(self.branch_label.get_label()) != 0) + def idle_stuff(self): + self.set_title(GLib.markup_escape_text(self.package.name)) + self.set_subtitle(self.package.app_id) + self.version_label.set_label(GLib.markup_escape_text(self.package.version)) + self.branch_label.set_label(GLib.markup_escape_text(self.package.branch)) + self.version_label.set_visible(len(self.version_label.get_label()) != 0) + self.branch_label.set_visible(len(self.branch_label.get_label()) != 0) - def set_state(self, state): - if state == self.state: - return + def set_state(self, state): + if state == self.state: + return - self.state = state - self.add_image.set_visible(False) - self.sub_image.set_visible(False) - self.selected_image.set_visible(False) - self.installed_image.set_visible(False) - match state: - case self.PackageState.NEW: - self.set_sensitive(True) - self.set_tooltip_text(_("Add Package to Queue")) - self.add_image.set_visible(True) - case self.PackageState.SELECTED: - self.set_sensitive(False) - self.set_tooltip_text(_("Package has been Added to Queue")) - self.selected_image.set_visible(True) - case self.PackageState.ADDED: - self.set_sensitive(True) - self.set_tooltip_text(_("Remove Package from Queue")) - self.sub_image.set_visible(True) - case self.PackageState.INSTALLED: - self.set_sensitive(False) - self.set_tooltip_text(_("This Package is Already Installed")) - self.installed_image.set_visible(True) + self.state = state + self.add_image.set_visible(False) + self.sub_image.set_visible(False) + self.selected_image.set_visible(False) + self.installed_image.set_visible(False) + match state: + case self.PackageState.NEW: + self.set_sensitive(True) + self.set_tooltip_text(_("Add Package to Queue")) + self.add_image.set_visible(True) + case self.PackageState.SELECTED: + self.set_sensitive(False) + self.set_tooltip_text(_("Package has been Added to Queue")) + self.selected_image.set_visible(True) + case self.PackageState.ADDED: + self.set_sensitive(True) + self.set_tooltip_text(_("Remove Package from Queue")) + self.sub_image.set_visible(True) + case self.PackageState.INSTALLED: + self.set_sensitive(False) + self.set_tooltip_text(_("This Package is Already Installed")) + self.installed_image.set_visible(True) - def __init__(self, package, package_state, origin_list_box, **kwargs): - super().__init__(**kwargs) + def __init__(self, package, package_state, origin_list_box, **kwargs): + super().__init__(**kwargs) - # Extra Object Creation - self.state = None - self.package = package - self.origin_list_box = origin_list_box + # Extra Object Creation + self.state = None + self.package = package + self.origin_list_box = origin_list_box - # Connections + # Connections - # Apply - GLib.idle_add(self.idle_stuff) - self.set_state(package_state) + # Apply + GLib.idle_add(self.idle_stuff) + self.set_state(package_state) diff --git a/src/install_page/results_page.blp b/src/install_page/results_page.blp index c22cb99..f84846a 100644 --- a/src/install_page/results_page.blp +++ b/src/install_page/results_page.blp @@ -2,53 +2,53 @@ using Gtk 4.0; using Adw 1; template $ResultsPage : Adw.NavigationPage { - title: _("Search a Remote"); - Adw.ToolbarView { - [top] - Adw.HeaderBar {} - [top] - Adw.Clamp { - maximum-size: 577; - margin-top: 3; - margin-bottom: 3; - margin-start: 6; - margin-end: 6; - SearchEntry search_entry { - search-delay: 500; - halign: fill; - // hexpand: true; - placeholder-text: _("Search for Packages"); - } - } - Stack stack { - Adw.StatusPage new_search { - icon-name: "loupe-large-symbolic"; - title: _("Search for Flatpaks"); - description: _("Search for Flatpaks you want to install"); - } - Adw.StatusPage too_many { - icon-name: "error-symbolic"; - title: _("Too Many Results"); - description: _("Try being more specific with your search"); - } - ScrolledWindow results_view { - Adw.Clamp { - ListBox results_list { - margin-start: 12; - margin-end: 12; - margin-top: 12; - margin-bottom: 12; - styles ["boxed-list"] - selection-mode: none; - valign: start; - } - } - } - Adw.StatusPage no_results { - icon-name: "loupe-large-symbolic"; - title: _("No Results Found"); - description: _("Try a different search term"); - } - } - } + title: _("Search a Remote"); + Adw.ToolbarView { + [top] + Adw.HeaderBar {} + [top] + Adw.Clamp { + maximum-size: 577; + margin-top: 3; + margin-bottom: 3; + margin-start: 6; + margin-end: 6; + SearchEntry search_entry { + search-delay: 500; + halign: fill; + // hexpand: true; + placeholder-text: _("Search for Packages"); + } + } + Stack stack { + Adw.StatusPage new_search { + icon-name: "loupe-large-symbolic"; + title: _("Search for Flatpaks"); + description: _("Search for Flatpaks you want to install"); + } + Adw.StatusPage too_many { + icon-name: "error-symbolic"; + title: _("Too Many Results"); + description: _("Try being more specific with your search"); + } + ScrolledWindow results_view { + Adw.Clamp { + ListBox results_list { + margin-start: 12; + margin-end: 12; + margin-top: 12; + margin-bottom: 12; + styles ["boxed-list"] + selection-mode: none; + valign: start; + } + } + } + Adw.StatusPage no_results { + icon-name: "loupe-large-symbolic"; + title: _("No Results Found"); + description: _("Try a different search term"); + } + } + } } diff --git a/src/install_page/results_page.py b/src/install_page/results_page.py index 7d48448..8815a34 100644 --- a/src/install_page/results_page.py +++ b/src/install_page/results_page.py @@ -6,137 +6,137 @@ from .error_toast import ErrorToast import subprocess class AddedPackage: - def __eq__(self, other): - return ( - self.name == other.name - and self.app_id == other.app_id - and self.branch == other.branch - and self.version == other.version - and self.remote == other.remote - and self.installation == other.installation - ) + def __eq__(self, other): + return ( + self.name == other.name + and self.app_id == other.app_id + and self.branch == other.branch + and self.version == other.version + and self.remote == other.remote + and self.installation == other.installation + ) - def is_similar(self, other): - return ( - self.app_id == other.app_id - and self.branch == other.branch - and self.version == other.version - ) + def is_similar(self, other): + return ( + self.app_id == other.app_id + and self.branch == other.branch + and self.version == other.version + ) - def __init__(self, name, app_id, branch, version, remote, installation): - self.name = name - self.app_id = app_id - self.branch = branch - self.version = version - self.remote = remote - self.installation = installation + def __init__(self, name, app_id, branch, version, remote, installation): + self.name = name + self.app_id = app_id + self.branch = branch + self.version = version + self.remote = remote + self.installation = installation @Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/results_page.ui") class ResultsPage(Adw.NavigationPage): - __gtype_name__ = "ResultsPage" - gtc = Gtk.Template.Child - - search_entry = gtc() - results_list = gtc() - stack = gtc() - new_search = gtc() - too_many = gtc() - results_view= gtc() - no_results = gtc() - - def show_remote(self, row, remote, installation, nav_view=None): - self.remote = remote - self.installation = installation - self.set_title(_("Search {}").format(remote.title)) - self.search_entry.set_text("") - self.search_entry.grab_focus() - if nav_view: - nav_view.push(self) - - def add_package_row(self, row): - self.pending_page.add_package_row(row) - if not self.install_page is None: - self.install_page.package_added() - - def on_search(self, *args): - self.packages.clear() - self.stack.set_visible_child(self.loading) - self.results_list.remove_all() - search_text = self.search_entry.get_text() - if search_text == "": - self.stack.set_visible_child(self.new_search) - return - - def thread(*args): - installation = "" - if self.installation == "user" or self.installation == "system": - installation = f"--{self.installation}" - else: - installation = f"--installation={self.installation}" - - try: - output = subprocess.run( - ['flatpak-spawn', '--host', 'flatpak', 'search', '--columns=all', installation, self.search_entry.get_text()], - check=True, text=True, capture_output=True - ).stdout.split('\n') - if len(output) > 100: - GLib.idle_add(lambda *_: self.stack.set_visible_child(self.too_many)) - return - - for line in output: - line = line.strip() - - info = line.split('\t') - if len(info) != 6: - continue - - remotes = info[5].split(',') - if not self.remote.name in remotes: - continue - - package = AddedPackage(info[0], info[2], info[4], info[3], self.remote, self.installation) - row = ResultRow(package, ResultRow.PackageState.NEW, self.results_list) - for item in self.pending_page.added_packages: - if package.is_similar(item): - row.set_state(ResultRow.PackageState.SELECTED) - - if package.app_id in HostInfo.id_to_flatpak: - installed_package = HostInfo.id_to_flatpak[package.app_id] - if installed_package.info["id"] == package.app_id and installed_package.info["branch"] == package.branch: - row.set_state(ResultRow.PackageState.INSTALLED) - - row.connect("activated", self.add_package_row) - self.packages.append(package) - GLib.idle_add(lambda *_, _row=row: self.results_list.append(_row)) - - if len(self.packages) > 0: - GLib.idle_add(lambda *_: self.stack.set_visible_child(self.results_view)) - else: - GLib.idle_add(lambda *_: self.stack.set_visible_child(self.no_results)) - - except subprocess.CalledProcessError as cpe: - GLib.idle_add(lambda *_, cpe=cpe: HostInfo.main_window.toast_overlay.add_toast(ErrorToast("Could not search for package", cpe.stderr).toast)) - GLib.idle_add(lambda *_: self.install_page.select_page.nav_view.pop()) - - Gio.Task().run_in_thread(thread) - - def on_back(self, *args): - self.results_list.remove_all() - self.stack.set_visible_child(self.new_search) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - # Extra Object Creation - self.remote = None - self.installation = None - self.packages = [] - self.pending_page = None - self.loading = LoadingStatus(_("Searching"), _("This should only take a moment")) - self.install_page = None - - # Connections - self.search_entry.connect("search-changed", self.on_search) - - # Apply - self.stack.add_child(self.loading) + __gtype_name__ = "ResultsPage" + gtc = Gtk.Template.Child + + search_entry = gtc() + results_list = gtc() + stack = gtc() + new_search = gtc() + too_many = gtc() + results_view= gtc() + no_results = gtc() + + def show_remote(self, row, remote, installation, nav_view=None): + self.remote = remote + self.installation = installation + self.set_title(_("Search {}").format(remote.title)) + self.search_entry.set_text("") + self.search_entry.grab_focus() + if nav_view: + nav_view.push(self) + + def add_package_row(self, row): + self.pending_page.add_package_row(row) + if not self.install_page is None: + self.install_page.package_added() + + def on_search(self, *args): + self.packages.clear() + self.stack.set_visible_child(self.loading) + self.results_list.remove_all() + search_text = self.search_entry.get_text() + if search_text == "": + self.stack.set_visible_child(self.new_search) + return + + def thread(*args): + installation = "" + if self.installation == "user" or self.installation == "system": + installation = f"--{self.installation}" + else: + installation = f"--installation={self.installation}" + + try: + output = subprocess.run( + ['flatpak-spawn', '--host', 'flatpak', 'search', '--columns=all', installation, self.search_entry.get_text()], + check=True, text=True, capture_output=True + ).stdout.split('\n') + if len(output) > 100: + GLib.idle_add(lambda *_: self.stack.set_visible_child(self.too_many)) + return + + for line in output: + line = line.strip() + + info = line.split('\t') + if len(info) != 6: + continue + + remotes = info[5].split(',') + if not self.remote.name in remotes: + continue + + package = AddedPackage(info[0], info[2], info[4], info[3], self.remote, self.installation) + row = ResultRow(package, ResultRow.PackageState.NEW, self.results_list) + for item in self.pending_page.added_packages: + if package.is_similar(item): + row.set_state(ResultRow.PackageState.SELECTED) + + if package.app_id in HostInfo.id_to_flatpak: + installed_package = HostInfo.id_to_flatpak[package.app_id] + if installed_package.info["id"] == package.app_id and installed_package.info["branch"] == package.branch: + row.set_state(ResultRow.PackageState.INSTALLED) + + row.connect("activated", self.add_package_row) + self.packages.append(package) + GLib.idle_add(lambda *_, _row=row: self.results_list.append(_row)) + + if len(self.packages) > 0: + GLib.idle_add(lambda *_: self.stack.set_visible_child(self.results_view)) + else: + GLib.idle_add(lambda *_: self.stack.set_visible_child(self.no_results)) + + except subprocess.CalledProcessError as cpe: + GLib.idle_add(lambda *_, cpe=cpe: HostInfo.main_window.toast_overlay.add_toast(ErrorToast("Could not search for package", cpe.stderr).toast)) + GLib.idle_add(lambda *_: self.install_page.select_page.nav_view.pop()) + + Gio.Task().run_in_thread(thread) + + def on_back(self, *args): + self.results_list.remove_all() + self.stack.set_visible_child(self.new_search) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # Extra Object Creation + self.remote = None + self.installation = None + self.packages = [] + self.pending_page = None + self.loading = LoadingStatus(_("Searching"), _("This should only take a moment")) + self.install_page = None + + # Connections + self.search_entry.connect("search-changed", self.on_search) + + # Apply + self.stack.add_child(self.loading) diff --git a/src/install_page/select_page.blp b/src/install_page/select_page.blp index 119ac22..53474c9 100644 --- a/src/install_page/select_page.blp +++ b/src/install_page/select_page.blp @@ -2,49 +2,49 @@ using Gtk 4.0; using Adw 1; template $SelectPage : Adw.NavigationPage { - title: _("Install Packages"); + title: _("Install Packages"); - Adw.ToastOverlay toast_overlay { - Adw.NavigationView nav_view { - Adw.NavigationPage select_nav_page { - title: _("Install Packages"); - Adw.ToolbarView { - [top] - Adw.HeaderBar { - [start] - $SidebarButton {} - } - Adw.PreferencesPage { - Adw.PreferencesGroup remotes_group { - title: _("Search in a Remote"); - description: _("Choose a remote to search for new packages"); - } - Adw.PreferencesGroup no_remotes { - title: _("Online Searches Disabled"); - description: _("Your system has no remotes added to search from"); - visible: bind remotes_group.visible inverted; - Adw.ActionRow add_remote_row { - title: _("Add a Remote"); - subtitle: _("Add a remote to your system to enable online searching"); - activatable: true; - [suffix] - Image { - icon-name: "right-large-symbolic"; - } - } - } - Adw.PreferencesGroup local_group { - title: _("Add Packages"); - description: _("Install packages from files on your system"); - Adw.ButtonRow open_row { - title: _("Open Files"); - start-icon-name: "folder-open-symbolic"; - } - } - } - } - } - $ResultsPage results_page {} - } - } + Adw.ToastOverlay toast_overlay { + Adw.NavigationView nav_view { + Adw.NavigationPage select_nav_page { + title: _("Install Packages"); + Adw.ToolbarView { + [top] + Adw.HeaderBar { + [start] + $SidebarButton {} + } + Adw.PreferencesPage { + Adw.PreferencesGroup remotes_group { + title: _("Search in a Remote"); + description: _("Choose a remote to search for new packages"); + } + Adw.PreferencesGroup no_remotes { + title: _("Online Searches Disabled"); + description: _("Your system has no remotes added to search from"); + visible: bind remotes_group.visible inverted; + Adw.ActionRow add_remote_row { + title: _("Add a Remote"); + subtitle: _("Add a remote to your system to enable online searching"); + activatable: true; + [suffix] + Image { + icon-name: "right-large-symbolic"; + } + } + } + Adw.PreferencesGroup local_group { + title: _("Add Packages"); + description: _("Install packages from files on your system"); + Adw.ButtonRow open_row { + title: _("Open Files"); + start-icon-name: "folder-open-symbolic"; + } + } + } + } + } + $ResultsPage results_page {} + } + } } diff --git a/src/install_page/select_page.py b/src/install_page/select_page.py index d080159..7fd6828 100644 --- a/src/install_page/select_page.py +++ b/src/install_page/select_page.py @@ -7,83 +7,83 @@ from .file_install_dialog import FileInstallDialog @Gtk.Template(resource_path="/io/github/flattool/Warehouse/install_page/select_page.ui") class SelectPage(Adw.NavigationPage): - __gtype_name__ = "SelectPage" - gtc = Gtk.Template.Child - - nav_view = gtc() - results_page = gtc() - remotes_group = gtc() - add_remote_row = gtc() - open_row = gtc() - - def start_loading(self): - self.nav_view.pop() - for row in self.remote_rows: - self.remotes_group.remove(row) - self.remote_rows.clear() - - def end_loading(self): - for installation, remotes in HostInfo.remotes.items(): - for remote in remotes: - if remote.disabled: - continue - - row = Adw.ActionRow(title=remote.title, subtitle=_("Installation: {}").format(installation), activatable=True) - row.add_suffix(Gtk.Image(icon_name="right-large-symbolic")) - row.connect("activated", self.results_page.show_remote, remote, installation, self.nav_view) - self.remotes_group.add(row) - self.remote_rows.append(row) - - self.remotes_group.set_visible(len(self.remote_rows) != 0) - - def local_install_apply_callback(self, installation, file_names): - install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row] - requests = [] - for file in file_names: - # sadly flatpak doesn't support multiple local installs in one command :( - requests.append({ - "remote": "local_file", - "installation": installation, - "package_names": [file.get_path()], - "extra_flags": [], - }) - - install_page.install_packages(requests) - - def file_dialog_handler(self, files): - FileInstallDialog(self, files, self.local_install_apply_callback).present(HostInfo.main_window) - - def file_choose_callback(self, object, result): - try: - files = object.open_multiple_finish(result) - if not files: - HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found to install"))) - return - - self.file_dialog_handler(files) - - except GLib.GError as gle: - if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2): - HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast) - - def on_open(self, *args): - file_filter = Gtk.FileFilter(name=_("Flatpaks")) - file_filter.add_suffix("flatpak") - file_filter.add_suffix("flatpakref") - filters = Gio.ListStore.new(Gtk.FileFilter) - filters.append(file_filter) - file_chooser = Gtk.FileDialog() - file_chooser.set_filters(filters) - file_chooser.set_default_filter(file_filter) - file_chooser.open_multiple(HostInfo.main_window, None, self.file_choose_callback) - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - # Extra Object Creation - self.remote_rows = [] - - # Connections - self.add_remote_row.connect("activated", lambda *_: HostInfo.main_window.activate_row(HostInfo.main_window.remotes_row)) - self.nav_view.connect("popped", self.results_page.on_back) - self.open_row.connect("activated", self.on_open) + __gtype_name__ = "SelectPage" + gtc = Gtk.Template.Child + + nav_view = gtc() + results_page = gtc() + remotes_group = gtc() + add_remote_row = gtc() + open_row = gtc() + + def start_loading(self): + self.nav_view.pop() + for row in self.remote_rows: + self.remotes_group.remove(row) + self.remote_rows.clear() + + def end_loading(self): + for installation, remotes in HostInfo.remotes.items(): + for remote in remotes: + if remote.disabled: + continue + + row = Adw.ActionRow(title=remote.title, subtitle=_("Installation: {}").format(installation), activatable=True) + row.add_suffix(Gtk.Image(icon_name="right-large-symbolic")) + row.connect("activated", self.results_page.show_remote, remote, installation, self.nav_view) + self.remotes_group.add(row) + self.remote_rows.append(row) + + self.remotes_group.set_visible(len(self.remote_rows) != 0) + + def local_install_apply_callback(self, installation, file_names): + install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row] + requests = [] + for file in file_names: + # sadly flatpak doesn't support multiple local installs in one command :( + requests.append({ + "remote": "local_file", + "installation": installation, + "package_names": [file.get_path()], + "extra_flags": [], + }) + + install_page.install_packages(requests) + + def file_dialog_handler(self, files): + FileInstallDialog(self, files, self.local_install_apply_callback).present(HostInfo.main_window) + + def file_choose_callback(self, object, result): + try: + files = object.open_multiple_finish(result) + if not files: + HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found to install"))) + return + + self.file_dialog_handler(files) + + except GLib.GError as gle: + if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2): + HostInfo.main_window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast) + + def on_open(self, *args): + file_filter = Gtk.FileFilter(name=_("Flatpaks")) + file_filter.add_suffix("flatpak") + file_filter.add_suffix("flatpakref") + filters = Gio.ListStore.new(Gtk.FileFilter) + filters.append(file_filter) + file_chooser = Gtk.FileDialog() + file_chooser.set_filters(filters) + file_chooser.set_default_filter(file_filter) + file_chooser.open_multiple(HostInfo.main_window, None, self.file_choose_callback) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # Extra Object Creation + self.remote_rows = [] + + # Connections + self.add_remote_row.connect("activated", lambda *_: HostInfo.main_window.activate_row(HostInfo.main_window.remotes_row)) + self.nav_view.connect("popped", self.results_page.on_back) + self.open_row.connect("activated", self.on_open) diff --git a/src/main.py b/src/main.py index b6a85bb..420eb13 100644 --- a/src/main.py +++ b/src/main.py @@ -31,228 +31,228 @@ from .const import Config from .error_toast import ErrorToast class WarehouseApplication(Adw.Application): - """The main application singleton class.""" - - troubleshooting = "OS: {os}\nWarehouse version: {wv}\nGTK: {gtk}\nlibadwaita: {adw}\nApp ID: {app_id}\nProfile: {profile}\nLanguage: {lang}" - version = Config.VERSION - - def __init__(self): - super().__init__( - application_id="io.github.flattool.Warehouse", - flags=Gio.ApplicationFlags.DEFAULT_FLAGS, - ) - self.create_action("about", self.on_about_action) - self.create_action("preferences", self.on_preferences_action) - self.create_action("quit", lambda *_: self.quit(), ["q"]) - self.create_action("open-menu", lambda *_: self.props.active_window.main_menu.popup(), ["F10"]) - self.create_action("refresh", lambda *_: self.props.active_window.refresh_handler(), ["r", "F5"]) - self.create_action("open-files", self.on_open_files_shortcut, ["o"]) - - self.create_action("show-packages-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("p"), ["p"]) - self.create_action("show-remotes-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("m"), ["m"]) - self.create_action("show-user-data-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("d"), ["d"]) - self.create_action("show-snapshots-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("s"), ["s"]) - self.create_action("show-install-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("i"), ["i"]) - - self.create_action("toggle-select-mode", self.on_toggle_select_mode_shortcut, ["b", "Return"]) - self.create_action("toggle-selection-kp-enter", self.on_toggle_select_mode_shortcut, ["KP_Enter"]) # Doesn't show in the shortcuts window - self.create_action("search-mode", self.on_search_mode_shortcut, ["f"]) - self.create_action("filter", self.on_filter_shortcut, ["t"]) - self.create_action("new", self.on_new_shortcut, ["n"]) - self.create_action("active-data-view", lambda *_: self.on_data_view_shortcut(True), ["1"]) - self.create_action("leftover-data-view", lambda *_: self.on_data_view_shortcut(False), ["2"]) - - self.is_dialog_open = False - - gtk_version = ( - str(Gtk.MAJOR_VERSION) - + "." - + str(Gtk.MINOR_VERSION) - + "." - + str(Gtk.MICRO_VERSION) - ) - adw_version = ( - str(Adw.MAJOR_VERSION) - + "." - + str(Adw.MINOR_VERSION) - + "." - + str(Adw.MICRO_VERSION) - ) - os_string = GLib.get_os_info("NAME") + " " + GLib.get_os_info("VERSION") - lang = GLib.environ_getenv(GLib.get_environ(), "LANG") - - self.troubleshooting = self.troubleshooting.format( - os=os_string, - wv=self.version, - gtk=gtk_version, - adw=adw_version, - profile=Config.PROFILE, - app_id=self.get_application_id(), - lang=lang, - ) - - def on_open_files_shortcut(self, *args): - window = self.props.active_window - - def file_choose_callback(object, result): - try: - files = object.open_multiple_finish(result) - if not files: - window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found")).toast) - return - - window.on_file_drop(None, files, None, None) - except GLib.GError as gle: - if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2): - window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast) - - file_filter = Gtk.FileFilter(name=_("Flatpaks & Remotes")) - file_filter.add_suffix("flatpak") - file_filter.add_suffix("flatpakref") - file_filter.add_suffix("flatpakrepo") - filters = Gio.ListStore.new(Gtk.FileFilter) - filters.append(file_filter) - file_chooser = Gtk.FileDialog() - file_chooser.set_filters(filters) - file_chooser.set_default_filter(file_filter) - file_chooser.open_multiple(window, None, file_choose_callback) - - def on_toggle_select_mode_shortcut(self, *args): - try: - button = self.props.active_window.stack.get_visible_child().select_button - button.set_active(not button.get_active()) - except AttributeError: - pass - - def on_search_mode_shortcut(self, *args): - try: - button = self.props.active_window.stack.get_visible_child().search_button - button.set_active(True) - except AttributeError: - pass - - def on_filter_shortcut(self, *args): - try: - button = self.props.active_window.stack.get_visible_child().filter_button - button.set_active(not button.get_active()) - except AttributeError: - pass - - try: - button = self.props.active_window.stack.get_visible_child().sort_button - button.set_active(True) - except AttributeError: - pass - - try: - button = self.props.active_window.stack.get_visible_child().show_disabled_button - if button.get_visible(): - button.set_active(not button.get_active()) - except AttributeError: - pass - - def on_new_shortcut(self, *args): - page = self.props.active_window.stack.get_visible_child() - try: - page.new_custom_handler() - except AttributeError: - pass - - try: - page.on_new() - except AttributeError: - pass - - def on_delete_shortcut(self, *args): - page = self.props.active_window.stack.get_visible_child() - try: - if not page.select_button.get_active(): - return - - page.select_trash_handler() - except AttributeError: - pass - - def on_data_view_shortcut(self, is_active): - page = self.props.active_window.stack.get_visible_child() - try: - adp = page.adp - ldp = page.ldp - page.stack.set_visible_child(adp if is_active else ldp) - except AttributeError: - pass - - def do_activate(self): - """Called when the application is activated. - - We raise the application's main window, creating it if - necessary. - """ - win = self.props.active_window - if not win: - win = WarehouseWindow(application=self) - win.present() - - def on_about_action(self, widget, _a): - """Callback for the app.about action.""" - about = Adw.AboutDialog( - application_name="Warehouse", - application_icon="io.github.flattool.Warehouse", - developer_name="Heliguy", - version=self.version, - developers=[ - "Heliguy https://github.com/heliguy4599", - "kramo https://kramo.page", - ], - artists=[ - "Heliguy https://github.com/heliguy4599", - "kramo https://kramo.page", - "Amy https://github.com/AtiusAmy", - "eryn https://github.com/hericiumvevo", - ], - copyright="© 2023 Heliguy", - license_type=Gtk.License.GPL_3_0_ONLY, - debug_info=self.troubleshooting, - # Translators: do one of the following, one per line: Your Name, Your Name , Your Name https://your.website - translator_credits=_("translator-credits"), - debug_info_filename="{}.txt".format(self.get_application_id()), - website="https://github.com/flattool/warehouse", - support_url="https://matrix.to/#/#warehouse-development:matrix.org", - issue_url="https://github.com/flattool/warehouse/issues", - ) - about.add_link(_("Donate"), "https://ko-fi.com/heliguy") - about.add_credit_section( - _("Contributors"), - [ - # Contributors: do one of the following, one per line: Your Name, Your Name , Your Name https://your.website - "Win ", - "Óscar Fernández Díaz", - "Runar https://github.com/runarcn", - "skøldis ", - "Maxim Therrien ", - ], - ) - about.present(self.props.active_window) - - def on_preferences_action(self, widget, _): - """Callback for the app.preferences action.""" - print("app.preferences action activated") - - def create_action(self, name, callback, shortcuts=None): - """Add an application action. - - Args: - name: the name of the action - callback: the function to be called when the action is activated - shortcuts: an optional list of accelerators - """ - action = Gio.SimpleAction.new(name, None) - action.connect("activate", callback) - self.add_action(action) - if shortcuts: - self.set_accels_for_action(f"app.{name}", shortcuts) - + """The main application singleton class.""" + + troubleshooting = "OS: {os}\nWarehouse version: {wv}\nGTK: {gtk}\nlibadwaita: {adw}\nApp ID: {app_id}\nProfile: {profile}\nLanguage: {lang}" + version = Config.VERSION + + def __init__(self): + super().__init__( + application_id="io.github.flattool.Warehouse", + flags=Gio.ApplicationFlags.DEFAULT_FLAGS, + ) + self.create_action("about", self.on_about_action) + self.create_action("preferences", self.on_preferences_action) + self.create_action("quit", lambda *_: self.quit(), ["q"]) + self.create_action("open-menu", lambda *_: self.props.active_window.main_menu.popup(), ["F10"]) + self.create_action("refresh", lambda *_: self.props.active_window.refresh_handler(), ["r", "F5"]) + self.create_action("open-files", self.on_open_files_shortcut, ["o"]) + + self.create_action("show-packages-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("p"), ["p"]) + self.create_action("show-remotes-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("m"), ["m"]) + self.create_action("show-user-data-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("d"), ["d"]) + self.create_action("show-snapshots-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("s"), ["s"]) + self.create_action("show-install-page", lambda *_: self.props.active_window.switch_page_shortcut_handler("i"), ["i"]) + + self.create_action("toggle-select-mode", self.on_toggle_select_mode_shortcut, ["b", "Return"]) + self.create_action("toggle-selection-kp-enter", self.on_toggle_select_mode_shortcut, ["KP_Enter"]) # Doesn't show in the shortcuts window + self.create_action("search-mode", self.on_search_mode_shortcut, ["f"]) + self.create_action("filter", self.on_filter_shortcut, ["t"]) + self.create_action("new", self.on_new_shortcut, ["n"]) + self.create_action("active-data-view", lambda *_: self.on_data_view_shortcut(True), ["1"]) + self.create_action("leftover-data-view", lambda *_: self.on_data_view_shortcut(False), ["2"]) + + self.is_dialog_open = False + + gtk_version = ( + str(Gtk.MAJOR_VERSION) + + "." + + str(Gtk.MINOR_VERSION) + + "." + + str(Gtk.MICRO_VERSION) + ) + adw_version = ( + str(Adw.MAJOR_VERSION) + + "." + + str(Adw.MINOR_VERSION) + + "." + + str(Adw.MICRO_VERSION) + ) + os_string = GLib.get_os_info("NAME") + " " + GLib.get_os_info("VERSION") + lang = GLib.environ_getenv(GLib.get_environ(), "LANG") + + self.troubleshooting = self.troubleshooting.format( + os=os_string, + wv=self.version, + gtk=gtk_version, + adw=adw_version, + profile=Config.PROFILE, + app_id=self.get_application_id(), + lang=lang, + ) + + def on_open_files_shortcut(self, *args): + window = self.props.active_window + + def file_choose_callback(object, result): + try: + files = object.open_multiple_finish(result) + if not files: + window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), _("No files were found")).toast) + return + + window.on_file_drop(None, files, None, None) + except GLib.GError as gle: + if not (gle.domain == "gtk-dialog-error-quark" and gle.code == 2): + window.toast_overlay.add_toast(ErrorToast(_("Could not add files"), str(gle)).toast) + + file_filter = Gtk.FileFilter(name=_("Flatpaks & Remotes")) + file_filter.add_suffix("flatpak") + file_filter.add_suffix("flatpakref") + file_filter.add_suffix("flatpakrepo") + filters = Gio.ListStore.new(Gtk.FileFilter) + filters.append(file_filter) + file_chooser = Gtk.FileDialog() + file_chooser.set_filters(filters) + file_chooser.set_default_filter(file_filter) + file_chooser.open_multiple(window, None, file_choose_callback) + + def on_toggle_select_mode_shortcut(self, *args): + try: + button = self.props.active_window.stack.get_visible_child().select_button + button.set_active(not button.get_active()) + except AttributeError: + pass + + def on_search_mode_shortcut(self, *args): + try: + button = self.props.active_window.stack.get_visible_child().search_button + button.set_active(True) + except AttributeError: + pass + + def on_filter_shortcut(self, *args): + try: + button = self.props.active_window.stack.get_visible_child().filter_button + button.set_active(not button.get_active()) + except AttributeError: + pass + + try: + button = self.props.active_window.stack.get_visible_child().sort_button + button.set_active(True) + except AttributeError: + pass + + try: + button = self.props.active_window.stack.get_visible_child().show_disabled_button + if button.get_visible(): + button.set_active(not button.get_active()) + except AttributeError: + pass + + def on_new_shortcut(self, *args): + page = self.props.active_window.stack.get_visible_child() + try: + page.new_custom_handler() + except AttributeError: + pass + + try: + page.on_new() + except AttributeError: + pass + + def on_delete_shortcut(self, *args): + page = self.props.active_window.stack.get_visible_child() + try: + if not page.select_button.get_active(): + return + + page.select_trash_handler() + except AttributeError: + pass + + def on_data_view_shortcut(self, is_active): + page = self.props.active_window.stack.get_visible_child() + try: + adp = page.adp + ldp = page.ldp + page.stack.set_visible_child(adp if is_active else ldp) + except AttributeError: + pass + + def do_activate(self): + """Called when the application is activated. + + We raise the application's main window, creating it if + necessary. + """ + win = self.props.active_window + if not win: + win = WarehouseWindow(application=self) + win.present() + + def on_about_action(self, widget, _a): + """Callback for the app.about action.""" + about = Adw.AboutDialog( + application_name="Warehouse", + application_icon="io.github.flattool.Warehouse", + developer_name="Heliguy", + version=self.version, + developers=[ + "Heliguy https://github.com/heliguy4599", + "kramo https://kramo.page", + ], + artists=[ + "Heliguy https://github.com/heliguy4599", + "kramo https://kramo.page", + "Amy https://github.com/AtiusAmy", + "eryn https://github.com/hericiumvevo", + ], + copyright="© 2023 Heliguy", + license_type=Gtk.License.GPL_3_0_ONLY, + debug_info=self.troubleshooting, + # Translators: do one of the following, one per line: Your Name, Your Name , Your Name https://your.website + translator_credits=_("translator-credits"), + debug_info_filename="{}.txt".format(self.get_application_id()), + website="https://github.com/flattool/warehouse", + support_url="https://matrix.to/#/#warehouse-development:matrix.org", + issue_url="https://github.com/flattool/warehouse/issues", + ) + about.add_link(_("Donate"), "https://ko-fi.com/heliguy") + about.add_credit_section( + _("Contributors"), + [ + # Contributors: do one of the following, one per line: Your Name, Your Name , Your Name https://your.website + "Win ", + "Óscar Fernández Díaz", + "Runar https://github.com/runarcn", + "skøldis ", + "Maxim Therrien ", + ], + ) + about.present(self.props.active_window) + + def on_preferences_action(self, widget, _): + """Callback for the app.preferences action.""" + print("app.preferences action activated") + + def create_action(self, name, callback, shortcuts=None): + """Add an application action. + + Args: + name: the name of the action + callback: the function to be called when the action is activated + shortcuts: an optional list of accelerators + """ + action = Gio.SimpleAction.new(name, None) + action.connect("activate", callback) + self.add_action(action) + if shortcuts: + self.set_accels_for_action(f"app.{name}", shortcuts) + def main(version): - """The application's entry point.""" - app = WarehouseApplication() - return app.run(sys.argv) + """The application's entry point.""" + app = WarehouseApplication() + return app.run(sys.argv) diff --git a/src/main_window/window.blp b/src/main_window/window.blp index 2f66843..f32ff9e 100644 --- a/src/main_window/window.blp +++ b/src/main_window/window.blp @@ -2,161 +2,161 @@ using Gtk 4.0; using Adw 1; template $WarehouseWindow: Adw.ApplicationWindow { - title: "Warehouse"; - width-request: 363; - default-width: 921; - default-height: 450; - Adw.Breakpoint main_breakpoint { - condition ("min-width: 865") - setters { - main_split.collapsed: false; - main_split.max-sidebar-width: 999999999; - } - } - content: - Adw.ToastOverlay toast_overlay { - Overlay { - [overlay] - Revealer file_drop_revealer { - can-target: false; - transition-type: crossfade; - Adw.StatusPage file_drop_view { - icon-name: "folder-open-symbolic"; - title: _("Drop to Open"); - description: _("Install Flatpaks or Add a Remote"); - styles ["drag-overlay-status-page"] - } - } - Adw.OverlaySplitView main_split { - collapsed: true; - show-sidebar: true; - sidebar-width-fraction: 0.2; - min-sidebar-width: 250; - sidebar: - Adw.NavigationPage { - title: "Warehouse"; - Adw.ToolbarView main_toolbar_view { - [top] - Adw.HeaderBar header_bar { - [start] - Button refresh_button { - icon-name: "arrow-circular-top-right-symbolic"; - tooltip-text: _("Refresh List"); - } - [end] - MenuButton main_menu { - icon-name: "open-menu-symbolic"; - tooltip-text: _("Main Menu"); - menu-model: primary_menu; - } - } - content: - ScrolledWindow { - ListBox navigation_row_listbox { - styles ["navigation-sidebar"] - Box packages_row { - margin-top: 12; - margin-bottom: 12; - margin-start: 6; - margin-end: 6; - spacing: 12; - Image icon { - icon-name: "flatpak-symbolic"; - } - Label { - label: _("Packages"); - } - } - Box remotes_row { - margin-top: 12; - margin-bottom: 12; - margin-start: 6; - margin-end: 6; - spacing: 12; - Image { - icon-name: "server-pick-symbolic"; - } - Label { - label: _("Remotes"); - } - } - - Box user_data_row { - margin-top: 12; - margin-bottom: 12; - margin-start: 6; - margin-end: 6; - spacing: 12; - Image { - icon-name: "file-manager-symbolic"; - } - Label { - label: _("User Data"); - } - } - Box snapshots_row { - margin-top: 12; - margin-bottom: 12; - margin-start: 6; - margin-end: 6; - spacing: 12; - Image { - icon-name: "snapshots-alt-symbolic"; - } - Label { - label: _("Snapshots"); - } - } - Box install_row { - margin-top: 12; - margin-bottom: 12; - margin-start: 6; - margin-end: 6; - spacing: 12; - Image { - icon-name: "arrow-pointing-at-line-down-symbolic"; - } - Label { - label: _("Install Packages"); - } - } - } - } - ; - } - } - ; - content: - Stack stack { - } - ; - } - } - } - ; + title: "Warehouse"; + width-request: 363; + default-width: 921; + default-height: 450; + Adw.Breakpoint main_breakpoint { + condition ("min-width: 865") + setters { + main_split.collapsed: false; + main_split.max-sidebar-width: 999999999; + } + } + content: + Adw.ToastOverlay toast_overlay { + Overlay { + [overlay] + Revealer file_drop_revealer { + can-target: false; + transition-type: crossfade; + Adw.StatusPage file_drop_view { + icon-name: "folder-open-symbolic"; + title: _("Drop to Open"); + description: _("Install Flatpaks or Add a Remote"); + styles ["drag-overlay-status-page"] + } + } + Adw.OverlaySplitView main_split { + collapsed: true; + show-sidebar: true; + sidebar-width-fraction: 0.2; + min-sidebar-width: 250; + sidebar: + Adw.NavigationPage { + title: "Warehouse"; + Adw.ToolbarView main_toolbar_view { + [top] + Adw.HeaderBar header_bar { + [start] + Button refresh_button { + icon-name: "arrow-circular-top-right-symbolic"; + tooltip-text: _("Refresh List"); + } + [end] + MenuButton main_menu { + icon-name: "open-menu-symbolic"; + tooltip-text: _("Main Menu"); + menu-model: primary_menu; + } + } + content: + ScrolledWindow { + ListBox navigation_row_listbox { + styles ["navigation-sidebar"] + Box packages_row { + margin-top: 12; + margin-bottom: 12; + margin-start: 6; + margin-end: 6; + spacing: 12; + Image icon { + icon-name: "flatpak-symbolic"; + } + Label { + label: _("Packages"); + } + } + Box remotes_row { + margin-top: 12; + margin-bottom: 12; + margin-start: 6; + margin-end: 6; + spacing: 12; + Image { + icon-name: "server-pick-symbolic"; + } + Label { + label: _("Remotes"); + } + } + + Box user_data_row { + margin-top: 12; + margin-bottom: 12; + margin-start: 6; + margin-end: 6; + spacing: 12; + Image { + icon-name: "file-manager-symbolic"; + } + Label { + label: _("User Data"); + } + } + Box snapshots_row { + margin-top: 12; + margin-bottom: 12; + margin-start: 6; + margin-end: 6; + spacing: 12; + Image { + icon-name: "snapshots-alt-symbolic"; + } + Label { + label: _("Snapshots"); + } + } + Box install_row { + margin-top: 12; + margin-bottom: 12; + margin-start: 6; + margin-end: 6; + spacing: 12; + Image { + icon-name: "arrow-pointing-at-line-down-symbolic"; + } + Label { + label: _("Install Packages"); + } + } + } + } + ; + } + } + ; + content: + Stack stack { + } + ; + } + } + } + ; } menu primary_menu { - section { - /*item { - label: _("_Preferences"); - action: "app.preferences"; - }*/ - // item { - // label: _("Refresh List"); - // action: "app.refresh-list"; - // } - item { - label: _("_Open Files"); - action: "app.open-files"; - } - item { - label: _("_Keyboard Shortcuts"); - action: "win.show-help-overlay"; - } - item { - label: _("_About Warehouse"); - action: "app.about"; - } - } + section { + /*item { + label: _("_Preferences"); + action: "app.preferences"; + }*/ + // item { + // label: _("Refresh List"); + // action: "app.refresh-list"; + // } + item { + label: _("_Open Files"); + action: "app.open-files"; + } + item { + label: _("_Keyboard Shortcuts"); + action: "win.show-help-overlay"; + } + item { + label: _("_About Warehouse"); + action: "app.about"; + } + } } diff --git a/src/main_window/window.py b/src/main_window/window.py index f9a9d34..32a35a2 100644 --- a/src/main_window/window.py +++ b/src/main_window/window.py @@ -28,218 +28,218 @@ from .const import Config @Gtk.Template(resource_path="/io/github/flattool/Warehouse/main_window/window.ui") class WarehouseWindow(Adw.ApplicationWindow): - __gtype_name__ = "WarehouseWindow" - gtc = Gtk.Template.Child - main_breakpoint = gtc() - toast_overlay = gtc() - file_drop_revealer = gtc() - main_split = gtc() - file_drop_view = gtc() - stack = gtc() - refresh_button = gtc() - main_menu = gtc() - navigation_row_listbox = gtc() - packages_row = gtc() - remotes_row = gtc() - user_data_row = gtc() - snapshots_row = gtc() - install_row = gtc() + __gtype_name__ = "WarehouseWindow" + gtc = Gtk.Template.Child + main_breakpoint = gtc() + toast_overlay = gtc() + file_drop_revealer = gtc() + main_split = gtc() + file_drop_view = gtc() + stack = gtc() + refresh_button = gtc() + main_menu = gtc() + navigation_row_listbox = gtc() + packages_row = gtc() + remotes_row = gtc() + user_data_row = gtc() + snapshots_row = gtc() + install_row = gtc() - def start_loading(self, *args): - for _, page in self.pages.items(): - if page.instance: - page.instance.start_loading() + def start_loading(self, *args): + for _, page in self.pages.items(): + if page.instance: + page.instance.start_loading() - def end_loading(self, *args): - for _, page in self.pages.items(): - if page.instance: - page.instance.end_loading() - - self.refresh_button.set_sensitive(True) - self.refresh_requested = False - self.remove_refresh_lockout("refresh handler direct") - - def do_refresh(self): - self.start_loading() - self.refresh_button.set_sensitive(False) - HostInfo.get_flatpaks(callback=self.end_loading) + def end_loading(self, *args): + for _, page in self.pages.items(): + if page.instance: + page.instance.end_loading() + + self.refresh_button.set_sensitive(True) + self.refresh_requested = False + self.remove_refresh_lockout("refresh handler direct") + + def do_refresh(self): + self.start_loading() + self.refresh_button.set_sensitive(False) + HostInfo.get_flatpaks(callback=self.end_loading) - def refresh_handler(self, *args): - if len(self.refresh_lockouts) == 0: - self.add_refresh_lockout("refresh handler direct") - self.do_refresh() - elif "refresh handler direct" in self.refresh_lockouts: - return - else: - self.refresh_requested = True - - def add_refresh_lockout(self, reason): - self.refresh_lockouts.append(reason) - self.refresh_button.set_sensitive(False) - - def remove_refresh_lockout(self, reason): - if reason in self.refresh_lockouts: - self.refresh_lockouts.remove(reason) - - if len(self.refresh_lockouts) == 0: - if self.refresh_requested: - self.do_refresh() - else: - self.refresh_button.set_sensitive(True) + def refresh_handler(self, *args): + if len(self.refresh_lockouts) == 0: + self.add_refresh_lockout("refresh handler direct") + self.do_refresh() + elif "refresh handler direct" in self.refresh_lockouts: + return + else: + self.refresh_requested = True + + def add_refresh_lockout(self, reason): + self.refresh_lockouts.append(reason) + self.refresh_button.set_sensitive(False) + + def remove_refresh_lockout(self, reason): + if reason in self.refresh_lockouts: + self.refresh_lockouts.remove(reason) + + if len(self.refresh_lockouts) == 0: + if self.refresh_requested: + self.do_refresh() + else: + self.refresh_button.set_sensitive(True) - def navigation_handler(self, _, row): - row = row.get_child() - page = self.pages[row] - self.stack.set_visible_child(page) - self.settings.set_string("page-shown", page.page_name) - if self.main_split.get_collapsed(): - self.main_split.set_show_sidebar(False) + def navigation_handler(self, _, row): + row = row.get_child() + page = self.pages[row] + self.stack.set_visible_child(page) + self.settings.set_string("page-shown", page.page_name) + if self.main_split.get_collapsed(): + self.main_split.set_show_sidebar(False) - def activate_row(self, nav_row): - idx = 0 - while row := self.navigation_row_listbox.get_row_at_index(idx): - idx += 1 - if row.get_child() is nav_row: - row.activate() - nav_row.grab_focus() - break + def activate_row(self, nav_row): + idx = 0 + while row := self.navigation_row_listbox.get_row_at_index(idx): + idx += 1 + if row.get_child() is nav_row: + row.activate() + nav_row.grab_focus() + break - def show_saved_page(self): - page_to_show = self.settings.get_string("page-shown") - page_found = False - for row, page in self.pages.items(): - self.stack.add_child(page) + def show_saved_page(self): + page_to_show = self.settings.get_string("page-shown") + page_found = False + for row, page in self.pages.items(): + self.stack.add_child(page) - if page.page_name == page_to_show: - page_found = True - self.activate_row(row) + if page.page_name == page_to_show: + page_found = True + self.activate_row(row) - if not page_found: - self.navigation_row_listbox.get_row_at_index(0).activate() - - def on_file_drop(self, drop_target, value, x, y): - try: - paks = [] - remotes = [] - for file in value: - path = file.get_path() - if path.endswith(".flatpak") or path.endswith(".flatpakref"): - paks.append(Gio.File.new_for_path(path)) - elif path.endswith(".flatpakrepo"): - remotes.append(path) - else: - dialog = Adw.AlertDialog( - heading=_("Unsupported Filetype"), - body=_("Only .flatpak, .flatpakref, and .flatpakrepo files are supported."), - ) - dialog.add_response("continue", _("OK")) - dialog.present(self) - return - - if len(remotes) > 0 and len(paks) > 0: - dialog = Adw.AlertDialog( - heading=_("Mixed Filetypes"), - body=_("Flatpaks and remotes cannot be installed at the same time."), - ) - dialog.add_css_class("error") - dialog.add_response("continue", _("OK")) - dialog.present(self) - return - - if len(remotes) > 1: - dialog = Adw.AlertDialog( - heading=_("Too Many Remotes"), - body=_("Only one remote at a time is supported."), - ) - dialog.add_response("continue", _("OK")) - dialog.present(self) - return - - if len(remotes) == 1: - # Adding a remote - self.activate_row(self.remotes_row) - remotes_page = self.pages[self.remotes_row] - remotes_page.local_file_handler(remotes[0]) - elif len(paks) > 0: - # Add packages - self.activate_row(self.install_row) - install_page = self.pages[self.install_row] - install_page.select_page.file_dialog_handler(paks) - - except Exception as e: - self.toast_overlay.add_toast(ErrorToast(_("Could not open files"), str(e)).toast) - - def on_drop_enter(self, *args): - self.main_split.add_css_class("blurred") - self.file_drop_revealer.set_reveal_child(True) - return 1 - - def on_drop_leave(self, *args): - self.main_split.remove_css_class("blurred") - self.file_drop_revealer.set_reveal_child(False) - - def switch_page_shortcut_handler(self, letter): - self.activate_row(self.shortcut_to_pages[letter]) - - def key_handler(self, controller, keyval, keycode, state): - page = self.stack.get_visible_child() - if keyval == Gdk.KEY_BackSpace or keyval == Gdk.KEY_Delete: - try: - if page.select_button.get_active(): - page.on_backspace_handler() - except AttributeError: - pass - elif keyval == Gdk.KEY_Escape: - try: - page.on_escape_handler() - except AttributeError: - pass - - def __init__(self, **kwargs): - super().__init__(**kwargs) + if not page_found: + self.navigation_row_listbox.get_row_at_index(0).activate() + + def on_file_drop(self, drop_target, value, x, y): + try: + paks = [] + remotes = [] + for file in value: + path = file.get_path() + if path.endswith(".flatpak") or path.endswith(".flatpakref"): + paks.append(Gio.File.new_for_path(path)) + elif path.endswith(".flatpakrepo"): + remotes.append(path) + else: + dialog = Adw.AlertDialog( + heading=_("Unsupported Filetype"), + body=_("Only .flatpak, .flatpakref, and .flatpakrepo files are supported."), + ) + dialog.add_response("continue", _("OK")) + dialog.present(self) + return + + if len(remotes) > 0 and len(paks) > 0: + dialog = Adw.AlertDialog( + heading=_("Mixed Filetypes"), + body=_("Flatpaks and remotes cannot be installed at the same time."), + ) + dialog.add_css_class("error") + dialog.add_response("continue", _("OK")) + dialog.present(self) + return + + if len(remotes) > 1: + dialog = Adw.AlertDialog( + heading=_("Too Many Remotes"), + body=_("Only one remote at a time is supported."), + ) + dialog.add_response("continue", _("OK")) + dialog.present(self) + return + + if len(remotes) == 1: + # Adding a remote + self.activate_row(self.remotes_row) + remotes_page = self.pages[self.remotes_row] + remotes_page.local_file_handler(remotes[0]) + elif len(paks) > 0: + # Add packages + self.activate_row(self.install_row) + install_page = self.pages[self.install_row] + install_page.select_page.file_dialog_handler(paks) + + except Exception as e: + self.toast_overlay.add_toast(ErrorToast(_("Could not open files"), str(e)).toast) + + def on_drop_enter(self, *args): + self.main_split.add_css_class("blurred") + self.file_drop_revealer.set_reveal_child(True) + return 1 + + def on_drop_leave(self, *args): + self.main_split.remove_css_class("blurred") + self.file_drop_revealer.set_reveal_child(False) + + def switch_page_shortcut_handler(self, letter): + self.activate_row(self.shortcut_to_pages[letter]) + + def key_handler(self, controller, keyval, keycode, state): + page = self.stack.get_visible_child() + if keyval == Gdk.KEY_BackSpace or keyval == Gdk.KEY_Delete: + try: + if page.select_button.get_active(): + page.on_backspace_handler() + except AttributeError: + pass + elif keyval == Gdk.KEY_Escape: + try: + page.on_escape_handler() + except AttributeError: + pass + + def __init__(self, **kwargs): + super().__init__(**kwargs) - # Extra Object Creation - HostInfo.main_window = self - ErrorToast.main_window = self - self.settings = Gio.Settings.new("io.github.flattool.Warehouse") - self.pages = { - self.packages_row: PackagesPage(main_window=self), - self.remotes_row: RemotesPage(main_window=self), - self.user_data_row: UserDataPage(main_window=self), - self.snapshots_row: SnapshotPage(main_window=self), - self.install_row: InstallPage(main_window=self), - } - self.shortcut_to_pages = { - "p": self.packages_row, - "m": self.remotes_row, - "d": self.user_data_row, - "s": self.snapshots_row, - "i": self.install_row, - } - self.navigation_row_listbox.connect("row-activated", self.navigation_handler) - self.show_saved_page() - self.refresh_lockouts = [] - self.refresh_requested = False - file_drop = Gtk.DropTarget.new(Gdk.FileList, Gdk.DragAction.COPY) - event_controller = Gtk.EventControllerKey() + # Extra Object Creation + HostInfo.main_window = self + ErrorToast.main_window = self + self.settings = Gio.Settings.new("io.github.flattool.Warehouse") + self.pages = { + self.packages_row: PackagesPage(main_window=self), + self.remotes_row: RemotesPage(main_window=self), + self.user_data_row: UserDataPage(main_window=self), + self.snapshots_row: SnapshotPage(main_window=self), + self.install_row: InstallPage(main_window=self), + } + self.shortcut_to_pages = { + "p": self.packages_row, + "m": self.remotes_row, + "d": self.user_data_row, + "s": self.snapshots_row, + "i": self.install_row, + } + self.navigation_row_listbox.connect("row-activated", self.navigation_handler) + self.show_saved_page() + self.refresh_lockouts = [] + self.refresh_requested = False + file_drop = Gtk.DropTarget.new(Gdk.FileList, Gdk.DragAction.COPY) + event_controller = Gtk.EventControllerKey() - # Apply - self.add_controller(file_drop) - self.add_controller(event_controller) - self.settings.bind("window-width", self, "default-width", Gio.SettingsBindFlags.DEFAULT) - self.settings.bind("window-height", self, "default-height", Gio.SettingsBindFlags.DEFAULT) - self.settings.bind("is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT) - self.settings.bind("is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT) - if Config.DEVEL: - self.add_css_class("devel") + # Apply + self.add_controller(file_drop) + self.add_controller(event_controller) + self.settings.bind("window-width", self, "default-width", Gio.SettingsBindFlags.DEFAULT) + self.settings.bind("window-height", self, "default-height", Gio.SettingsBindFlags.DEFAULT) + self.settings.bind("is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT) + self.settings.bind("is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT) + if Config.DEVEL: + self.add_css_class("devel") - # Connections - file_drop.connect("drop", self.on_file_drop) - file_drop.connect("enter", self.on_drop_enter) - file_drop.connect("leave", self.on_drop_leave) - event_controller.connect("key-pressed", self.key_handler) - self.refresh_button.connect("clicked", self.refresh_handler) - - # Apply again - self.start_loading() - HostInfo.get_flatpaks(callback=self.end_loading) + # Connections + file_drop.connect("drop", self.on_file_drop) + file_drop.connect("enter", self.on_drop_enter) + file_drop.connect("leave", self.on_drop_leave) + event_controller.connect("key-pressed", self.key_handler) + self.refresh_button.connect("clicked", self.refresh_handler) + + # Apply again + self.start_loading() + HostInfo.get_flatpaks(callback=self.end_loading) diff --git a/src/properties_page/properties_page.blp b/src/properties_page/properties_page.blp index ff9ab0a..b83af1b 100644 --- a/src/properties_page/properties_page.blp +++ b/src/properties_page/properties_page.blp @@ -240,89 +240,89 @@ template $PropertiesPage : Adw.NavigationPage { } } Adw.PreferencesGroup remote_info { - margin-bottom: 12; - title: _("Installation Information"); - Adw.ActionRow sdk_row { - styles ["property"] - title: "SDK"; - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - Adw.ActionRow origin_row { - styles ["property"] - title: _("Origin"); - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - Adw.ActionRow collection_row { - styles ["property"] - title: _("Collection"); - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - Adw.ActionRow installation_row { - styles ["property"] - title: _("Installation"); - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - } - Adw.PreferencesGroup commit_info { - title: _("Commit Information"); - Adw.ActionRow commit_row { - styles ["property"] - title: "Commit"; - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - Adw.ActionRow parent_row { - styles ["property"] - title: _("Parent"); - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - Adw.ActionRow subject_row { - styles ["property"] - title: _("Subject"); - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - Adw.ActionRow date_row { - styles ["property"] - title: _("Date"); - activatable: true; - Image { - icon-name: "copy-symbolic"; - } - } - } - } - } - } - } - } - } - } - } - } + margin-bottom: 12; + title: _("Installation Information"); + Adw.ActionRow sdk_row { + styles ["property"] + title: "SDK"; + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + Adw.ActionRow origin_row { + styles ["property"] + title: _("Origin"); + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + Adw.ActionRow collection_row { + styles ["property"] + title: _("Collection"); + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + Adw.ActionRow installation_row { + styles ["property"] + title: _("Installation"); + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + } + Adw.PreferencesGroup commit_info { + title: _("Commit Information"); + Adw.ActionRow commit_row { + styles ["property"] + title: "Commit"; + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + Adw.ActionRow parent_row { + styles ["property"] + title: _("Parent"); + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + Adw.ActionRow subject_row { + styles ["property"] + title: _("Subject"); + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + Adw.ActionRow date_row { + styles ["property"] + title: _("Date"); + activatable: true; + Image { + icon-name: "copy-symbolic"; + } + } + } + } + } + } + } + } + } + } + } + } } Popover more_menu { - styles ["menu"] - ListBox more_list { - } + styles ["menu"] + ListBox more_list { + } }