diff --git a/src/install_page/install_page.blp b/src/install_page/install_page.blp index a877a06..04a86e8 100644 --- a/src/install_page/install_page.blp +++ b/src/install_page/install_page.blp @@ -11,6 +11,8 @@ template $InstallPage : Adw.BreakpointBin { setters { split_view.collapsed: true; split_view.show-content: false; + results_action_bar.visible: true; + pending_action_bar.visible: true; } } @@ -48,11 +50,29 @@ template $InstallPage : Adw.BreakpointBin { Adw.ToolbarView { [top] Adw.HeaderBar { + [start] + ToggleButton sidebar_button { + icon-name: "dock-left-symbolic"; + tooltip-text: _("Show Sidebar"); + } } Adw.PreferencesPage { Adw.PreferencesGroup remotes_group { - title: "Choose a Remote"; - description: "Choose a remote to search for packages in"; + title: _("Search in a Remote"); + description: _("Choose a remote to search for new packages"); + } + Adw.PreferencesGroup local_group { + title: _("Add a File"); + description: _("Install a package from a file on your system"); + Adw.ActionRow open_row { + title: _("Open"); + subtitle: _("Add the file you want to install"); + activatable: true; + [suffix] + Image { + icon-name: "folder-open-symbolic"; + } + } } } } @@ -64,17 +84,6 @@ template $InstallPage : Adw.BreakpointBin { Adw.ToolbarView sidebar_tbv { [top] Adw.HeaderBar header_bar { - show-back-button: false; - [start] - ToggleButton sidebar_button { - icon-name: "dock-left-symbolic"; - tooltip-text: _("Show Sidebar"); - } - [start] - Button back_button { - icon-name: "left-large-symbolic"; - tooltip-text: _("Back"); - } } [top] Adw.Clamp { @@ -109,6 +118,22 @@ template $InstallPage : Adw.BreakpointBin { } } } + [bottom] + ActionBar results_action_bar { + visible: false; + revealed: false; + [center] + Button review_button { + sensitive: bind results_action_bar.revealed; + margin-top: 3; + margin-bottom: 3; + styles ["pill", "suggested-action"] + Adw.ButtonContent { + icon-name: "view-list-bullet-symbolic"; + label: _("Review and Install"); + } + } + } } } } @@ -116,41 +141,36 @@ template $InstallPage : Adw.BreakpointBin { } ; content: - Adw.NavigationPage results_page { + Adw.NavigationPage pending_page { title: _("Pending Packages"); Adw.ToolbarView { [top] Adw.HeaderBar { - [start] - Button clear_button { - label: _("Remove All"); + } + Stack pending_stack { + Adw.StatusPage no_added_packages_status { + icon-name: "flatpak-symbolic"; + title: _("Install New Packages"); + description: _("Search for a package or add one from a file"); + } + Adw.PreferencesPage added_pref_page { } } - Adw.PreferencesPage added_pref_page { - - } + // Button open_button { + // visible: false; + // Adw.ButtonContent { + // can-shrink: true; + // icon-name: "folder-open-symbolic"; + // label: _("Add File"); + // } + // } [bottom] - Box { - homogeneous: true; - styles ["toolbar"] - Button remove_all_button { - styles ["raised"] - Adw.ButtonContent { - can-shrink: true; - icon-name: "minus-large-symbolic"; - label: _("Remove All"); - } - } - Button open_button { - styles ["raised"] - Adw.ButtonContent { - can-shrink: true; - icon-name: "folder-open-symbolic"; - label: _("Add File"); - } - } + ActionBar pending_action_bar { + revealed: false; + [center] Button install_button { - styles ["raised", "suggested-action"] + sensitive: bind pending_action_bar.revealed; + styles ["pill", "suggested-action"] Adw.ButtonContent { can-shrink: true; icon-name: "arrow-pointing-at-line-down-symbolic"; @@ -163,4 +183,4 @@ template $InstallPage : Adw.BreakpointBin { ; } } -} \ No newline at end of file +} diff --git a/src/install_page/install_page.py b/src/install_page/install_page.py index 305d323..baaa9e1 100644 --- a/src/install_page/install_page.py +++ b/src/install_page/install_page.py @@ -7,6 +7,23 @@ from .result_row import ResultRow import os, 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 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 @@ -18,8 +35,6 @@ class AddedPackage: class AddedGroup(Adw.PreferencesGroup): __gtype_name__ = "AddedGroup" - package_rows = [] - def add_row(self, row): self.package_rows.append(row) self.add(row) @@ -35,6 +50,7 @@ class AddedGroup(Adw.PreferencesGroup): self.remote = remote self.installation = installation + self.package_rows = [] self.set_title(f"{remote.title}") self.set_description(_("Installation: {}").format(installation)) @@ -47,11 +63,17 @@ class InstallPage(Adw.BreakpointBin): remotes_group = gtc() sb_page_view = gtc() results = gtc() - back_button = gtc() search_entry = gtc() search_apply_button = gtc() results_list = gtc() added_pref_page = gtc() + results_action_bar = gtc() + pending_action_bar = gtc() + review_button = gtc() + split_view = gtc() + sidebar_button = gtc() + pending_stack = gtc() + no_added_packages_status = gtc() # Referred to in the main window # It is used to determine if a new page should be made or not @@ -62,12 +84,23 @@ class InstallPage(Adw.BreakpointBin): current_remote = None def start_loading(self): + self.pending_stack.set_visible_child(self.no_added_packages_status) + self.added_packages.clear() + for row in self.remote_rows: self.remotes_group.remove(row) self.remote_rows.clear() - self.added_packages.clear() + + for _, group in self.added_package_groups.items(): + self.added_pref_page.remove(group) self.added_package_groups.clear() + self.results_action_bar.set_revealed(False) + self.pending_action_bar.set_revealed(False) + self.search_entry.set_text("") + self.results_list.remove_all() + self.sb_page_view.pop() + def end_loading(self): for installation in HostInfo.installations: for remote in HostInfo.remotes[installation]: @@ -84,8 +117,9 @@ class InstallPage(Adw.BreakpointBin): self.current_remote = remote self.results.set_title(_("Search {}").format(remote.title)) self.sb_page_view.push(self.results) - self.search_entry.set_text("firefox") self.search_entry.grab_focus() + self.search_entry.set_text("") + self.results_list.remove_all() def on_search(self, _): text = self.search_entry.get_text().strip().lower().replace(" ", "") @@ -103,7 +137,7 @@ class InstallPage(Adw.BreakpointBin): try: output = subprocess.run(['flatpak-spawn', '--host', 'flatpak', 'search', '--columns=all', installation, text], text=True, check=True, capture_output=True).stdout.split("\n") - for i, line in enumerate(output): + for line in output: info = line.split("\t") if len(info) != 6: continue @@ -118,83 +152,104 @@ class InstallPage(Adw.BreakpointBin): if not self.current_remote.name in remotes.split(','): continue - is_added = False - try: - for package in self.added_packages: - if package.name == name and package.app_id == app_id and package.version == version and package.branch == branch: - is_added = True - break + current = AddedPackage(name, app_id, branch, version, self.current_remote, self.current_installation) + row = ResultRow(current) + row.connect("activated", self.add_package) + self.results_list.append(row) + + for other in self.added_packages: + if current.is_similar(other): + row.set_is_added(True) + break - except KeyError: - print("passing key error") - - if not is_added: - row = ResultRow(name, app_id, branch, version) - row.connect("activated", self.add_package, i, name, app_id, branch, version) - self.results_list.append(row) + except subprocess.CalledProcessError as cpe: + print(cpe.stderr) except Exception as e: print(e) - except subprocess.CalledProcessError as cpe: - print(cpe) - + thread() + def add_package(self, row): + row.set_is_added(True) + self.added_packages.append(row.package) + self.list_focus_grabber(row) + added_row = ResultRow(row.package, True) + self.results_action_bar.set_revealed(True) + self.pending_action_bar.set_revealed(True) + try: + key = f"{row.package.remote}<>{row.package.installation}" + group = self.added_package_groups[key] + group.add_row(added_row) + except KeyError: + group = AddedGroup(row.package.remote, row.package.installation) + group.add_row(added_row) + self.added_package_groups[key] = group + self.added_pref_page.add(group) + + added_row.connect("activated", self.rem_package, group) + self.pending_stack.set_visible_child(self.added_pref_page) + + def rem_package(self, row, group): + if not row.package in self.added_packages: + return + + self.added_packages.remove(row.package) + if len(self.added_packages) == 0: + self.results_action_bar.set_revealed(False) + self.pending_action_bar.set_revealed(False) + + i = 0 + while rover := self.results_list.get_row_at_index(i): + i += 1 + if not rover.is_added: + continue + + if row.package.is_similar(rover.package): + rover.set_is_added(False) + break + + group.rem_row(row) + if len(group.package_rows) == 0: + self.added_pref_page.remove(group) + self.added_package_groups.pop(f'{row.package.remote}<>{row.package.installation}', None) + + if len(self.added_package_groups) == 0: + self.pending_stack.set_visible_child(self.no_added_packages_status) + def list_focus_grabber(self, row): i = 0 - prev_visible_row = None + prev_unadded_row = None while rover := self.results_list.get_row_at_index(i): i += 1 if rover is row: break - - if rover.get_visible(): - prev_visible_row = rover + + if not rover.is_added: + prev_unadded_row = rover while rover := self.results_list.get_row_at_index(i): i += 1 - if rover.get_visible(): + if not rover.is_added: rover.grab_focus() return - if prev_visible_row: - prev_visible_row.grab_focus() - - def add_package(self, row, index, name, app_id, branch, version): - key = f"{self.current_remote}<>{self.current_installation}" - package = AddedPackage(name, app_id, branch, version, self.current_remote, self.current_installation) - added_row = ResultRow(name, app_id, branch, version, row) - self.added_packages.append(package) - group = None - try: - group = self.added_package_groups[key] - group.add_row(added_row) - except KeyError: - group = AddedGroup(self.current_remote, self.current_installation) - self.added_package_groups[key] = group - self.added_pref_page.add(group) - group.add_row(added_row) - except Exception as e: - print(e) - return - added_row.connect("activated", self.remove_package, group) - row.set_visible(False) - self.list_focus_grabber(row) - - def remove_package(self, row, group): - row.original_row.set_visible(True) - group.rem_row(row) - if len(group.package_rows) == 0: - group.set_visible(False) + if prev_unadded_row: + prev_unadded_row.grab_focus() def __init__(self, main_window, **kwargs): super().__init__(**kwargs) self.instance = self self.main_window = main_window self.remote_rows = [] - self.added_packages = [] # remote<>installation + self.added_packages = [] self.added_package_groups = {} # remote<>installation - self.back_button.connect("clicked", lambda *_: self.sb_page_view.pop()) + # self.back_button.connect("clicked", lambda *_: self.sb_page_view.pop()) # self.search_entry.connect("search-changed", self.on_search) self.search_entry.connect("activate", self.on_search) - self.search_apply_button.connect("clicked", self.on_search) \ No newline at end of file + self.search_apply_button.connect("clicked", self.on_search) + self.review_button.connect("clicked", lambda *_: self.split_view.set_show_content(True)) + + ms = main_window.main_split + ms.connect("notify::show-sidebar", lambda *_: self.sidebar_button.set_active(ms.get_show_sidebar())) + self.sidebar_button.connect("toggled", lambda *_: ms.set_show_sidebar(self.sidebar_button.get_active())) diff --git a/src/install_page/result_row.blp b/src/install_page/result_row.blp index 4312df1..73c969f 100644 --- a/src/install_page/result_row.blp +++ b/src/install_page/result_row.blp @@ -5,6 +5,7 @@ template $ResultRow : Adw.ActionRow { activatable: true; title: "No title set"; subtitle: "No subtitle set"; + tooltip-text: _("Add Package to Queue"); Box { orientation: vertical; @@ -36,4 +37,15 @@ template $ResultRow : Adw.ActionRow { Image sub_image { icon-name: "minus-large-symbolic"; } -} \ No newline at end of file + [suffix] + Image selected_image { + icon-name: "check-plain-symbolic"; + visible: false; + } + [suffix] + Image already_installed_image { + visible: false; + icon-name: "selection-mode-symbolic"; + styles ["success"] + } +} diff --git a/src/install_page/result_row.py b/src/install_page/result_row.py index 75d615a..6373ea2 100644 --- a/src/install_page/result_row.py +++ b/src/install_page/result_row.py @@ -1,4 +1,4 @@ -from gi.repository import Adw, Gtk, GLib, Gio +from gi.repository import Adw, Gtk, GLib, Gio, Gdk from .host_info import HostInfo from .error_toast import ErrorToast import os, subprocess @@ -12,25 +12,32 @@ class ResultRow(Adw.ActionRow): branch_label = gtc() add_image = gtc() sub_image = gtc() + selected_image = gtc() def idle_stuff(self): - self.set_title(GLib.markup_escape_text(self.name)) - self.set_subtitle(self.app_id) - self.version_label.set_label(GLib.markup_escape_text(self.version)) - self.branch_label.set_label(GLib.markup_escape_text(self.branch)) + 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) - self.sub_image.set_visible(bool(self.original_row)) - self.add_image.set_visible(not bool(self.original_row)) - self.set_tooltip_text(_("Remove Package from Queue") if bool(self.original_row) else _("Add Package to Queue")) + if self.is_added: + self.set_tooltip_text(_("Remove Package from Queue")) - def __init__(self, name, app_id, branch, version, original_row=None, **kwargs): + def set_is_added(self, is_added): + self.is_added = is_added + self.set_sensitive(not is_added) + self.add_image.set_visible(not is_added) + self.selected_image.set_visible(is_added) + self.set_tooltip_text(_("This package is queued") if is_added else _("Add Package to Queue")) + + def __init__(self, package, is_added=False, **kwargs): super().__init__(**kwargs) - self.name = name - self.app_id = app_id - self.branch = branch - self.version = version - self.original_row = original_row + self.is_added = is_added + self.package = package - GLib.idle_add(self.idle_stuff) \ No newline at end of file + self.sub_image.set_visible(is_added) + self.add_image.set_visible(not is_added) + + GLib.idle_add(self.idle_stuff)