diff --git a/bottles/frontend/bottle-details-view-subpage.blp b/bottles/frontend/bottle-details-view-subpage.blp new file mode 100644 index 0000000000..967cd6d1d0 --- /dev/null +++ b/bottles/frontend/bottle-details-view-subpage.blp @@ -0,0 +1,55 @@ +using Gtk 4.0; +using Adw 1; + +template $BottleDetailsViewSubpage: Adw.NavigationPage { + title: bind content_title.title; + tag: "subpage"; + + child: Adw.ToolbarView { + [top] + Adw.HeaderBar content_headerbar { + show-start-title-buttons: false; + + title-widget: Adw.WindowTitle content_title {}; + + [end] + Box box_actions {} + + [end] + MenuButton btn_operations { + visible: false; + tooltip-text: _("Operations"); + popover: pop_tasks; + + Spinner spinner_tasks {} + + styles [ + "flat", + ] + } + } + + content: Stack stack_bottle { + transition-type: crossfade; + }; + }; +} + +Popover pop_tasks { + Box { + orientation: vertical; + spacing: 3; + + Box { + orientation: vertical; + + ListBox list_tasks { + selection-mode: none; + + styles [ + "content", + ] + } + } + } +} diff --git a/bottles/frontend/bottle-details-view.blp b/bottles/frontend/bottle-details-view.blp index 5c4797a0ad..a54fd93e93 100644 --- a/bottles/frontend/bottle-details-view.blp +++ b/bottles/frontend/bottle-details-view.blp @@ -1,103 +1,17 @@ using Gtk 4.0; using Adw 1; -template $BottleDetailsView: Adw.Bin { - Adw.Leaflet leaflet { - can-navigate-back: true; - can-unfold: false; - hexpand: true; - - Box { - orientation: vertical; - - Adw.HeaderBar sidebar_headerbar { - show-end-title-buttons: false; - - title-widget: Adw.WindowTitle sidebar_title { - title: _("Details"); - }; - - [start] - Button btn_back { - icon-name: "go-previous-symbolic"; - tooltip-text: _("Go Back"); - } - - [end] - Box default_actions {} - } - - Box default_view {} +template $BottleDetailsView: Adw.NavigationPage { + title: _("Details"); + tag: "details"; + + child: Adw.ToolbarView { + [top] + Adw.HeaderBar { + [end] + Box default_actions {} } - Adw.LeafletPage { - navigatable: false; - - child: Separator panel_separator { - orientation: vertical; - - styles [ - "sidebar", - ] - }; - } - - Box content { - orientation: vertical; - - Adw.HeaderBar content_headerbar { - show-start-title-buttons: false; - - title-widget: Adw.WindowTitle content_title {}; - - [start] - Button btn_back_sidebar { - icon-name: "go-previous-symbolic"; - tooltip-text: _("Go Back"); - visible: false; - } - - [end] - Box box_actions {} - - [end] - MenuButton btn_operations { - visible: false; - tooltip-text: _("Operations"); - popover: pop_tasks; - - Spinner spinner_tasks {} - - styles [ - "flat", - ] - } - } - - Stack stack_bottle { - transition-type: crossfade; - hexpand: true; - vexpand: true; - } - } - } -} - -Popover pop_tasks { - Box { - orientation: vertical; - spacing: 3; - - Box { - orientation: vertical; - - ListBox list_tasks { - selection-mode: none; - - styles [ - "content", - ] - } - } - } + content: Box default_view {}; + }; } diff --git a/bottles/frontend/bottle_details_page.py b/bottles/frontend/bottle_details_page.py index 57e44ee046..9d03659e35 100644 --- a/bottles/frontend/bottle_details_page.py +++ b/bottles/frontend/bottle_details_page.py @@ -106,8 +106,7 @@ def __init__(self, details, config, **kwargs): # common variables and references self.window = details.window self.manager = details.window.manager - self.stack_bottle = details.stack_bottle - self.leaflet = details.leaflet + self.stack_bottle = details.details_view_subpage.stack_bottle self.details = details self.config = config self.show_hidden = False @@ -154,10 +153,12 @@ def __change_page(self, _widget, page_name): the page is not available, it will show the "bottle" page. """ if page_name == "taskmanager": - self.details.view_taskmanager.update(config=self.config) + self.details.details_view_subpage.view_taskmanager.update( + config=self.config + ) try: self.stack_bottle.set_visible_child_name(page_name) - self.leaflet.navigate(Adw.NavigationDirection.FORWARD) + self.window.main_leaf.push(self.details.details_view_subpage) except: # pylint: disable=bare-except pass diff --git a/bottles/frontend/bottle_details_view.py b/bottles/frontend/bottle_details_view.py index afe50ab1f8..8a567cb859 100644 --- a/bottles/frontend/bottle_details_view.py +++ b/bottles/frontend/bottle_details_view.py @@ -33,68 +33,48 @@ from bottles.frontend.details_task_manager_view import DetailsTaskManagerView -@Gtk.Template(resource_path="/com/usebottles/bottles/bottle-details-view.ui") -class BottleDetailsView(Adw.Bin): - """ - This class is the starting point for all the pages concerning the - bottle (details, preferences, dependencies ..). - """ - - __gtype_name__ = "BottleDetailsView" - __pages = {} +@Gtk.Template(resource_path="/com/usebottles/bottles/bottle-details-view-subpage.ui") +class BottleDetailsViewSubpage(Adw.NavigationPage): + __gtype_name__ = "BottleDetailsViewSubpage" - # region Widgets - leaflet = Gtk.Template.Child() - default_view = Gtk.Template.Child() - stack_bottle = Gtk.Template.Child() - sidebar_headerbar = Gtk.Template.Child() - content_headerbar = Gtk.Template.Child() - default_actions = Gtk.Template.Child() - box_actions = Gtk.Template.Child() - content_title = Gtk.Template.Child() - btn_back = Gtk.Template.Child() - btn_back_sidebar = Gtk.Template.Child() - btn_operations = Gtk.Template.Child() - list_tasks = Gtk.Template.Child() - pop_tasks = Gtk.Template.Child() spinner_tasks = Gtk.Template.Child() + pop_tasks = Gtk.Template.Child() + list_tasks = Gtk.Template.Child() + btn_operations = Gtk.Template.Child() + content_title = Gtk.Template.Child() + box_actions = Gtk.Template.Child() + content_headerbar = Gtk.Template.Child() + stack_bottle = Gtk.Template.Child() # endregion - def __init__(self, window, config: BottleConfig | None = None, **kwargs): + def __init__( + self, details_view, window, config: BottleConfig | None = None, **kwargs + ): super().__init__(**kwargs) - # common variables and references - if config is None: - config = BottleConfig() - + self.details_view = details_view self.window = window - self.manager = window.manager - self.versioning_manager = window.manager.versioning_manager self.config = config - self.queue = QueueManager(add_fn=self.lock_back, end_fn=self.unlock_back) - self.view_bottle = BottleDetailsPage(self, config) - self.view_installers = DetailsInstallersView(self, config) - self.view_dependencies = DetailsDependenciesView(self, config) - self.view_preferences = DetailsPreferencesPage(self, config) - self.view_versioning = DetailsVersioningPage(self, config) - self.view_taskmanager = DetailsTaskManagerView(self, config) - - self.btn_back.connect("clicked", self.go_back) - self.btn_back_sidebar.connect("clicked", self.go_back_sidebar) - self.window.main_leaf.connect("notify::visible-child", self.unload_view) - self.default_actions.append(self.view_bottle.actions) - - # region signals - self.stack_bottle.connect("notify::visible-child", self.__on_page_change) self.btn_operations.connect("activate", self.__on_operations_toggled) self.btn_operations.connect("notify::visible", self.__spin_tasks_toggle) - self.leaflet.connect("notify::folded", self.__on_leaflet_folded) - # endregion + self.stack_bottle.connect("notify::visible-child", self.__on_page_change) RunAsync(self.build_pages) + def __spin_tasks_toggle(self, widget, *_args): + if widget.get_visible(): + self.spinner_tasks.start() + self.spinner_tasks.set_visible(True) + else: + self.spinner_tasks.stop() + self.spinner_tasks.set_visible(False) + + def __on_operations_toggled(self, widget): + if not self.list_tasks.get_first_child(): + widget.set_visible(False) + def set_title(self, title, subtitle: str = ""): """ This function is used to set the title of the BottleDetailsView @@ -103,17 +83,24 @@ def set_title(self, title, subtitle: str = ""): self.content_title.set_title(title) self.content_title.set_subtitle(subtitle) - def __on_leaflet_folded(self, widget, *_args): - folded = widget.get_folded() - self.sidebar_headerbar.set_show_end_title_buttons(folded) - self.content_headerbar.set_show_start_title_buttons(folded) - self.btn_back_sidebar.set_visible(folded) + def set_actions(self, widget: Gtk.Widget = None): + """ + This function is used to set the actions buttons in the headerbar. + """ + while self.box_actions.get_first_child(): + self.box_actions.remove(self.box_actions.get_first_child()) + + if widget: + self.box_actions.append(widget) + + def unload_view(self, *_args): + while self.stack_bottle.get_first_child(): + self.stack_bottle.remove(self.stack_bottle.get_first_child()) def __on_page_change(self, *_args): """ Update headerbar title according to the current page. """ - self.window.toggle_selection_mode(False) page = self.stack_bottle.get_visible_child_name() self.set_title(self.__pages[page]["title"], self.__pages[page]["description"]) @@ -163,7 +150,7 @@ def build_pages(self): def ui_update(): if self.view_bottle.get_parent() is None: - self.default_view.append(self.view_bottle) + self.details_view.default_view.append(self.view_bottle) self.stack_bottle.add_named(self.view_preferences, "preferences") self.stack_bottle.add_named(self.view_dependencies, "dependencies") @@ -176,17 +163,53 @@ def ui_update(): GLib.idle_add(ui_update) - def set_actions(self, widget: Gtk.Widget = None): - """ - This function is used to set the actions buttons in the headerbar. - """ - while self.box_actions.get_first_child(): - self.box_actions.remove(self.box_actions.get_first_child()) - if widget: - self.box_actions.append(widget) +@Gtk.Template(resource_path="/com/usebottles/bottles/bottle-details-view.ui") +class BottleDetailsView(Adw.NavigationPage): + """ + This class is the starting point for all the pages concerning the + bottle (details, preferences, dependencies ..). + """ + + __gtype_name__ = "BottleDetailsView" + __pages = {} + + # region Widgets + default_view = Gtk.Template.Child() + default_actions = Gtk.Template.Child() + + # endregion + + def __init__(self, window, config: BottleConfig | None = None, **kwargs): + super().__init__(**kwargs) - def set_config(self, config: BottleConfig, rebuild_pages=True): + # common variables and references + if config is None: + config = BottleConfig() + + self.window = window + self.manager = window.manager + self.versioning_manager = window.manager.versioning_manager + self.config = config + self.queue = QueueManager(add_fn=self.lock_back, end_fn=self.unlock_back) + + self.details_view_subpage = BottleDetailsViewSubpage(self, window, config) + self.details_view_subpage.view_bottle = BottleDetailsPage(self, config) + self.details_view_subpage.view_installers = DetailsInstallersView(self, config) + self.details_view_subpage.view_dependencies = DetailsDependenciesView( + self, config + ) + self.details_view_subpage.view_preferences = DetailsPreferencesPage( + self, config + ) + self.details_view_subpage.view_versioning = DetailsVersioningPage(self, config) + self.details_view_subpage.view_taskmanager = DetailsTaskManagerView( + self, config + ) + + self.default_actions.append(self.details_view_subpage.view_bottle.actions) + + def set_config(self, config: BottleConfig): """ This function update widgets according to the bottle configuration. It also temporarily disable the functions @@ -196,46 +219,17 @@ def set_config(self, config: BottleConfig, rebuild_pages=True): self.config = config # update widgets data with bottle configuration - self.view_bottle.set_config(config=config) - self.view_preferences.set_config(config=config) - self.view_taskmanager.set_config(config=config) - self.view_installers.update(config=config) - self.view_versioning.update(config=config) - - if rebuild_pages: - self.build_pages() - - def __on_operations_toggled(self, widget): - if not self.list_tasks.get_first_child(): - widget.set_visible(False) - - def __spin_tasks_toggle(self, widget, *_args): - if widget.get_visible(): - self.spinner_tasks.start() - self.spinner_tasks.set_visible(True) - else: - self.spinner_tasks.stop() - self.spinner_tasks.set_visible(False) - - def go_back(self, _widget=False): - self.window.main_leaf.navigate(Adw.NavigationDirection.BACK) - - def go_back_sidebar(self, *_args): - self.leaflet.navigate(Adw.NavigationDirection.BACK) - - def unload_view(self, *_args): - while self.stack_bottle.get_first_child(): - self.stack_bottle.remove(self.stack_bottle.get_first_child()) + self.details_view_subpage.view_bottle.set_config(config=config) + self.details_view_subpage.view_preferences.set_config(config=config) + self.details_view_subpage.view_taskmanager.set_config(config=config) + self.details_view_subpage.view_installers.update(config=config) + self.details_view_subpage.view_versioning.update(config=config) @GtkUtils.run_in_main_loop - def lock_back(self): - self.btn_back.set_sensitive(False) - self.btn_back.set_tooltip_text(_("Operations in progress, please wait.")) + def lock_back(self): ... @GtkUtils.run_in_main_loop - def unlock_back(self): - self.btn_back.set_sensitive(True) - self.btn_back.set_tooltip_text(_("Return to your bottles.")) + def unlock_back(self): ... def update_runner_label(self, runner: str): - self.view_bottle.label_runner.set_text(runner) + self.details_view_subpage.view_bottle.label_runner.set_text(runner) diff --git a/bottles/frontend/bottles-list-view.blp b/bottles/frontend/bottles-list-view.blp index 299ca52141..dab0859f67 100644 --- a/bottles/frontend/bottles-list-view.blp +++ b/bottles/frontend/bottles-list-view.blp @@ -1,68 +1,49 @@ using Gtk 4.0; using Adw 1; -template $BottlesListView: Adw.Bin { - ScrolledWindow { - Box { - hexpand: true; - vexpand: true; - orientation: vertical; +template $BottlesListView: Stack { + StackPage { + name: "bottles-list-page"; + child: Adw.PreferencesPage { + Adw.PreferencesGroup group_bottles { + ListBox list_bottles { + selection-mode: none; - SearchBar search_bar { - SearchEntry entry_search { - placeholder-text: _("Search your bottles…"); - } - } - - Adw.PreferencesPage pref_page { - Adw.PreferencesGroup group_bottles { - ListBox list_bottles { - selection-mode: none; - - styles [ - "boxed-list", - ] - } - } - - Adw.PreferencesGroup group_steam { - title: _("Steam Proton"); - - ListBox list_steam { - selection-mode: none; - - styles [ - "boxed-list", - ] - } + styles [ + "boxed-list", + ] } } - Adw.StatusPage bottle_status { - title: _("Bottles"); - hexpand: true; - vexpand: true; + Adw.PreferencesGroup group_steam { + title: _("Steam Proton"); - child: Button btn_create { - valign: center; - halign: center; - label: _("Create New Bottle…"); + ListBox list_steam { + selection-mode: none; styles [ - "suggested-action", - "pill", + "boxed-list", ] - }; + } } + }; + } - Adw.StatusPage no_bottles_found { - visible: false; - icon-name: "system-search-symbolic"; - title: _("No Results Found"); - description: _("Try a different search."); - hexpand: true; - vexpand: true; - } - } + StackPage { + name: "empty-page"; + child: Adw.StatusPage { + title: _("Bottles"); + + child: Button create_button { + valign: center; + halign: center; + label: _("Create New Bottle…"); + + styles [ + "suggested-action", + "pill", + ] + }; + }; } } diff --git a/bottles/frontend/bottles.gresource.xml b/bottles/frontend/bottles.gresource.xml index de4e8a7636..ad2404316d 100644 --- a/bottles/frontend/bottles.gresource.xml +++ b/bottles/frontend/bottles.gresource.xml @@ -24,6 +24,7 @@ local-resource-row.ui exclusion-pattern-row.ui bottle-details-view.ui + bottle-details-view-subpage.ui bottle-details-page.ui details-dependencies-view.ui details-installers-view.ui diff --git a/bottles/frontend/bottles_list_view.py b/bottles/frontend/bottles_list_view.py index e2cffa18c2..c7538cb8d8 100644 --- a/bottles/frontend/bottles_list_view.py +++ b/bottles/frontend/bottles_list_view.py @@ -115,7 +115,7 @@ def set_path(_dialog, response): def show_details(self, widget=None, config=None): if config is None: config = self.config - self.window.page_details.view_preferences.update_combo_components() + self.window.page_details.details_view_subpage.view_preferences.update_combo_components() self.window.show_details_view(config=config) def disable(self): @@ -124,7 +124,7 @@ def disable(self): @Gtk.Template(resource_path="/com/usebottles/bottles/bottles-list-view.ui") -class BottlesListView(Adw.Bin): +class BottlesListView(Gtk.Stack): __gtype_name__ = "BottlesListView" __bottles = {} @@ -133,12 +133,7 @@ class BottlesListView(Adw.Bin): list_steam = Gtk.Template.Child() group_bottles = Gtk.Template.Child() group_steam = Gtk.Template.Child() - pref_page = Gtk.Template.Child() - bottle_status = Gtk.Template.Child() - btn_create = Gtk.Template.Child() - entry_search = Gtk.Template.Child() - search_bar = Gtk.Template.Child() - no_bottles_found = Gtk.Template.Child() + create_button = Gtk.Template.Child() # endregion @@ -150,32 +145,18 @@ def __init__(self, window, arg_bottle=None, **kwargs): self.arg_bottle = arg_bottle # connect signals - self.btn_create.connect("clicked", self.window.show_add_view) - self.entry_search.connect("changed", self.__search_bottles) + self.create_button.connect("clicked", self.window.show_add_view) # backend signals SignalManager.connect( Signals.ManagerLocalBottlesLoaded, self.update_bottles_list ) - self.bottle_status.set_icon_name(APP_ID) + status_page = self.get_child_by_name("empty-page") + status_page.set_icon_name(APP_ID) self.update_bottles_list() - def __search_bottles(self, widget, event=None, data=None): - """ - This function search in the list of bottles the - text written in the search entry. - """ - terms = widget.get_text() - self.list_bottles.set_filter_func(self.__filter_bottles, terms) - self.list_steam.set_filter_func(self.__filter_bottles, terms) - - @staticmethod - def __filter_bottles(row, terms=None): - text = row.get_title().lower() - return terms.lower() in text - def update_bottles_list(self, *args) -> None: self.__bottles = {} while self.list_bottles.get_first_child(): @@ -187,8 +168,11 @@ def update_bottles_list(self, *args) -> None: local_bottles = self.window.manager.local_bottles is_empty_local_bottles = len(local_bottles) == 0 - self.pref_page.set_visible(not is_empty_local_bottles) - self.bottle_status.set_visible(is_empty_local_bottles) + if is_empty_local_bottles: + self.set_visible_child_name("empty-page") + return + + self.set_visible_child_name("bottles-list-page") for name, config in local_bottles.items(): _entry = BottleRow(self.window, config) diff --git a/bottles/frontend/details-versioning-page.blp b/bottles/frontend/details-versioning-page.blp index 99bc65a74b..8217093e6e 100644 --- a/bottles/frontend/details-versioning-page.blp +++ b/bottles/frontend/details-versioning-page.blp @@ -1,24 +1,32 @@ using Gtk 4.0; using Adw 1; -template $DetailsVersioningPage: Adw.PreferencesPage { - Adw.PreferencesPage pref_page { - Adw.PreferencesGroup { - ListBox list_states { - selection-mode: none; +template $DetailsVersioningPage: Adw.Bin { + child: Stack { + StackPage { + name: "states-list-page"; + child: Adw.PreferencesPage { + Adw.PreferencesGroup { + ListBox list_states { + selection-mode: none; - styles [ - "boxed-list", - ] - } + styles [ + "boxed-list", + ] + } + } + }; } - } - Adw.StatusPage status_page { - icon-name: "preferences-system-time-symbolic"; - title: _("No Snapshots Found"); - description: _("Create your first snapshot to start saving states of your preferences."); - } + StackPage { + name: "empty-page"; + child: Adw.StatusPage { + icon-name: "preferences-system-time-symbolic"; + title: _("No Snapshots Found"); + description: _("Create your first snapshot to start saving states of your preferences."); + }; + } + }; } Popover pop_context { diff --git a/bottles/frontend/details_versioning_page.py b/bottles/frontend/details_versioning_page.py index d83408f328..d09d313387 100644 --- a/bottles/frontend/details_versioning_page.py +++ b/bottles/frontend/details_versioning_page.py @@ -28,7 +28,7 @@ @Gtk.Template(resource_path="/com/usebottles/bottles/details-versioning-page.ui") -class DetailsVersioningPage(Adw.PreferencesPage): +class DetailsVersioningPage(Adw.Bin): __gtype_name__ = "DetailsVersioningPage" __registry = [] @@ -39,8 +39,6 @@ class DetailsVersioningPage(Adw.PreferencesPage): btn_save = Gtk.Template.Child() btn_help = Gtk.Template.Child() entry_state_message = Gtk.Template.Child() - status_page = Gtk.Template.Child() - pref_page = Gtk.Template.Child() btn_add = Gtk.Template.Child() ev_controller = Gtk.EventControllerKey.new() @@ -62,6 +60,8 @@ def __init__(self, details, config, **kwargs): self.btn_help.connect("clicked", open_doc_url, "bottles/versioning") self.entry_state_message.connect("activate", self.add_state) + self.stack = self.get_child() + def empty_list(self): for r in self.__registry: if r.get_parent() is not None: @@ -99,10 +99,10 @@ def new_state(_state, active): self.list_states.append(entry) def callback(result, error=False): - self.status_page.set_visible(not result.status) - self.pref_page.set_visible(result.status) - self.list_states.set_visible(result.status) - self.list_states.set_sensitive(result.status) + if result.status: + self.stack.set_visible_child_name("states-list-page") + else: + self.stack.set_visible_child_name("empty-page") def process_states(): GLib.idle_add(self.empty_list) diff --git a/bottles/frontend/importer-view.blp b/bottles/frontend/importer-view.blp index 7cd80ccd7b..3dd9b43f5b 100644 --- a/bottles/frontend/importer-view.blp +++ b/bottles/frontend/importer-view.blp @@ -1,19 +1,13 @@ using Gtk 4.0; using Adw 1; -template $ImporterView: Adw.Bin { - Box { - orientation: vertical; - - HeaderBar headerbar { - title-widget: Adw.WindowTitle window_title {}; - - [start] - Button btn_back { - tooltip-text: _("Go Back"); - icon-name: "go-previous-symbolic"; - } +template $ImporterView: Adw.NavigationPage { + title: _("Import"); + tag: "import"; + child: Adw.ToolbarView { + [top] + Adw.HeaderBar { [end] Box box_actions { MenuButton btn_import_backup { @@ -29,25 +23,31 @@ template $ImporterView: Adw.Bin { } } - Adw.PreferencesPage { - Adw.StatusPage status_page { - vexpand: true; - icon-name: "document-save-symbolic"; - title: _("No Prefixes Found"); - description: _("No external prefixes were found. Does Bottles have access to them?\nUse the icon on the top to import a bottle from a backup."); - } + content: Stack stack { + StackPage { + name: "importer-page"; + child: Adw.PreferencesPage { + Adw.PreferencesGroup group_prefixes { - Adw.PreferencesGroup group_prefixes { - visible: false; + ListBox list_prefixes { + styles [ + "boxed-list", + ] + } + } + }; + } - ListBox list_prefixes { - styles [ - "boxed-list", - ] - } + StackPage { + name: "empty-page"; + child: Adw.StatusPage { + icon-name: "document-save-symbolic"; + title: _("No Prefixes Found"); + description: _("No external prefixes were found. Does Bottles have access to them? Use the icon on the top to import a bottle from a backup."); + }; } - } - } + }; + }; } Popover pop_backup { diff --git a/bottles/frontend/importer_view.py b/bottles/frontend/importer_view.py index 4a2256f220..f6186abab0 100644 --- a/bottles/frontend/importer_view.py +++ b/bottles/frontend/importer_view.py @@ -27,7 +27,7 @@ @Gtk.Template(resource_path="/com/usebottles/bottles/importer-view.ui") -class ImporterView(Adw.Bin): +class ImporterView(Adw.NavigationPage): __gtype_name__ = "ImporterView" # region Widgets @@ -35,9 +35,8 @@ class ImporterView(Adw.Bin): btn_find_prefixes = Gtk.Template.Child() btn_import_config = Gtk.Template.Child() btn_import_full = Gtk.Template.Child() - btn_back = Gtk.Template.Child() group_prefixes = Gtk.Template.Child() - status_page = Gtk.Template.Child() + stack = Gtk.Template.Child() # endregion @@ -50,12 +49,13 @@ def __init__(self, window, **kwargs): self.import_manager = window.manager.import_manager # connect signals - self.btn_back.connect("clicked", self.go_back) self.btn_find_prefixes.connect("clicked", self.__find_prefixes) self.btn_import_full.connect("clicked", self.__import_full_bck) self.btn_import_config.connect("clicked", self.__import_config_bck) - def __find_prefixes(self, widget): + self.__find_prefixes() + + def __find_prefixes(self, *args): """ This function remove all entries from the list_prefixes, ask the manager to find all prefixes in the system and add them to the list @@ -63,14 +63,13 @@ def __find_prefixes(self, widget): @GtkUtils.run_in_main_loop def update(result, error=False): - widget.set_sensitive(True) if result.ok: wineprefixes = result.data.get("wineprefixes") if len(wineprefixes) == 0: + self.stack.set_visible_child_name("empty-page") return - self.status_page.set_visible(False) - self.group_prefixes.set_visible(True) + self.stack.set_visible_child_name("importer-page") while self.list_prefixes.get_first_child(): _w = self.list_prefixes.get_first_child() @@ -79,8 +78,6 @@ def update(result, error=False): for prefix in result.data.get("wineprefixes"): self.list_prefixes.append(ImporterRow(self, prefix)) - widget.set_sensitive(False) - RunAsync(self.import_manager.search_wineprefixes, callback=update) @GtkUtils.run_in_main_loop @@ -162,6 +159,3 @@ def set_path(_dialog, response): dialog.set_modal(True) dialog.connect("response", set_path) dialog.show() - - def go_back(self, *_args): - self.window.main_leaf.navigate(Adw.NavigationDirection.BACK) diff --git a/bottles/frontend/installer_dialog.py b/bottles/frontend/installer_dialog.py index 775c1648ff..bbfd54f2de 100644 --- a/bottles/frontend/installer_dialog.py +++ b/bottles/frontend/installer_dialog.py @@ -188,8 +188,7 @@ def set_status(result, error=False): def __installed(self): self.set_deletable(False) self.stack.set_visible_child_name("page_installed") - self.window.page_details.view_bottle.update_programs() - self.window.page_details.go_back_sidebar() + self.window.page_details.details_view_subpage.view_bottle.update_programs() def __error(self, error): self.set_deletable(True) diff --git a/bottles/frontend/main.py b/bottles/frontend/main.py index 35855da872..69f43658a6 100644 --- a/bottles/frontend/main.py +++ b/bottles/frontend/main.py @@ -299,7 +299,7 @@ def __new_bottle(self, *args): self.win.show_add_view() def __show_importer_view(self, widget=False, *args): - self.win.main_leaf.set_visible_child(self.win.page_importer) + self.win.main_leaf.push(self.win.page_importer) def __show_about_dialog(self, *_args): developers = [ diff --git a/bottles/frontend/meson.build b/bottles/frontend/meson.build index 5bf84dd5d2..ce461f4c3e 100644 --- a/bottles/frontend/meson.build +++ b/bottles/frontend/meson.build @@ -19,6 +19,7 @@ blueprints = custom_target('blueprints', 'details-task-manager-view.blp', 'details-versioning-page.blp', 'bottle-details-view.blp', + 'bottle-details-view-subpage.blp', 'bottle-picker-dialog.blp', 'crash-report-dialog.blp', 'dependencies-check-dialog.blp', diff --git a/bottles/frontend/operation.py b/bottles/frontend/operation.py index 086519a5e8..0dee315ab5 100644 --- a/bottles/frontend/operation.py +++ b/bottles/frontend/operation.py @@ -59,11 +59,13 @@ def __init__(self, window): def _new_widget(self, title, cancellable=True) -> TaskRow: """create TaskRow widget & add to task list""" task_entry = TaskRow(self.window, title, cancellable) - self.window.page_details.list_tasks.append(task_entry) + self.window.page_details.details_view_subpage.list_tasks.append(task_entry) return task_entry def _set_task_btn_visible(self, visible: bool): - self.window.page_details.btn_operations.set_visible(visible) + self.window.page_details.details_view_subpage.btn_operations.set_visible( + visible + ) def task_added_handler(self, res: Result): """handler for Signals.TaskAdded""" @@ -86,7 +88,9 @@ def task_removed_handler(self, res: Result): if task_id not in self._TASK_WIDGETS: return - self.window.page_details.list_tasks.remove(self._TASK_WIDGETS[task_id]) + self.window.page_details.details_view_subpage.list_tasks.remove( + self._TASK_WIDGETS[task_id] + ) del self._TASK_WIDGETS[task_id] if len(self._TASK_WIDGETS) == 0: diff --git a/bottles/frontend/program_row.py b/bottles/frontend/program_row.py index 620cd041e5..001d962e4b 100644 --- a/bottles/frontend/program_row.py +++ b/bottles/frontend/program_row.py @@ -65,7 +65,7 @@ def __init__( # common variables and references self.window = window - self.view_bottle = window.page_details.view_bottle + self.view_bottle = window.page_details.details_view_subpage.view_bottle self.manager = window.manager self.config = config self.program = program diff --git a/bottles/frontend/window.blp b/bottles/frontend/window.blp index cc3bf7155f..901e732b8e 100644 --- a/bottles/frontend/window.blp +++ b/bottles/frontend/window.blp @@ -5,73 +5,87 @@ template $BottlesWindow: Adw.ApplicationWindow { title: "Bottles"; close-request => $on_close_request(); - Adw.ToastOverlay toasts { - Adw.Leaflet main_leaf { - can-unfold: false; - can-navigate-back: false; - - Box { - orientation: vertical; - - HeaderBar headerbar { - title-widget: Adw.ViewSwitcherTitle view_switcher_title { - title: "Bottles"; - stack: stack_main; - }; + Adw.Breakpoint { + condition ("max-width: 450sp") + setters { + headerbar.title-widget: null; + view_switcher_bar.reveal: true; + } + } - Button btn_add { - tooltip-text: _("Create New Bottle"); - icon-name: "list-add-symbolic"; + content: Adw.ToastOverlay toasts { + Adw.NavigationView main_leaf { + Adw.NavigationPage { + tag: "overview-page"; + title: _("Overview"); + child: Adw.ToolbarView { + [top] + Adw.HeaderBar headerbar { + title-widget: Adw.ViewSwitcher view_switcher_title { + policy: wide; + stack: stack_main; + }; + + Button btn_add { + tooltip-text: _("Create New Bottle"); + icon-name: "list-add-symbolic"; + } + + Box box_actions {} + + [end] + MenuButton { + icon-name: "open-menu-symbolic"; + menu-model: primary_menu; + tooltip-text: _("Main Menu"); + primary: true; + } + + [end] + ToggleButton btn_search { + tooltip-text: _("Search"); + icon-name: "system-search-symbolic"; + visible: false; + } + + [end] + Button btn_donate { + tooltip-text: _("Donate"); + icon-name: "emblem-favorite-symbolic"; + } + + [end] + Button btn_noconnection { + visible: false; + tooltip-text: _("You don\'t seem connected to the internet. Without it you will not be able to download essential components. Click this icon when you have reestablished the connection."); + icon-name: "network-error-symbolic"; + } } - Box box_actions {} - - styles [ - "titlebar", - ] - - [end] - MenuButton { - icon-name: "open-menu-symbolic"; - menu-model: primary_menu; - tooltip-text: _("Main Menu"); - primary: true; - } + [top] + Adw.Clamp { + maximum-size: 400; - [end] - ToggleButton btn_search { - tooltip-text: _("Search"); - icon-name: "system-search-symbolic"; - visible: false; + child: SearchBar search_bar { + SearchEntry entry_search { + hexpand: true; + placeholder-text: _("Search your bottles…"); + } + }; } - [end] - Button btn_donate { - tooltip-text: _("Donate"); - icon-name: "emblem-favorite-symbolic"; - } + content: Adw.ViewStack stack_main { + enable-transitions: true; + }; - [end] - Button btn_noconnection { - visible: false; - tooltip-text: _("You don\'t seem connected to the internet. Without it you will not be able to download essential components. Click this icon when you have reestablished the connection."); - icon-name: "network-error-symbolic"; + [bottom] + Adw.ViewSwitcherBar view_switcher_bar { + stack: stack_main; } - } - - SearchBar searchbar {} - - Adw.ViewStack stack_main { - vexpand: true; - } - - Adw.ViewSwitcherBar view_switcher_bar { - stack: stack_main; - reveal: bind view_switcher_title.title-visible; - } + }; } } - } + }; } menu primary_menu { diff --git a/bottles/frontend/window.py b/bottles/frontend/window.py index c627e8de9b..5c2eeac31d 100644 --- a/bottles/frontend/window.py +++ b/bottles/frontend/window.py @@ -60,11 +60,11 @@ class BottlesWindow(Adw.ApplicationWindow): btn_donate = Gtk.Template.Child() btn_noconnection = Gtk.Template.Child() box_actions = Gtk.Template.Child() - headerbar = Gtk.Template.Child() view_switcher_title = Gtk.Template.Child() - view_switcher_bar = Gtk.Template.Child() main_leaf = Gtk.Template.Child() toasts = Gtk.Template.Child() + entry_search = Gtk.Template.Child() + search_bar = Gtk.Template.Child() # endregion # Common variables @@ -132,7 +132,6 @@ def response(dialog, response, *args): self.stack_main.add_named( child=self.page_loading, name="page_loading" ).set_visible(False) - self.headerbar.add_css_class("flat") # Signal connections self.btn_donate.connect( @@ -143,6 +142,7 @@ def response(dialog, response, *args): self.btn_add.connect("clicked", self.show_add_view) self.btn_noconnection.connect("clicked", self.check_for_connection) self.stack_main.connect("notify::visible-child", self.__on_page_changed) + self.entry_search.connect("changed", self.__search_bottles) # backend signal handlers self.task_syncer = TaskSyncer(self) @@ -224,11 +224,8 @@ def set_manager(result: Manager, error=None): self.page_importer = ImporterView(self) self.page_library = LibraryView(self) - self.main_leaf.append(self.page_details) - self.main_leaf.append(self.page_importer) - - self.main_leaf.get_page(self.page_details).set_navigatable(False) - self.main_leaf.get_page(self.page_importer).set_navigatable(False) + self.main_leaf.add(self.page_details) + self.main_leaf.add(self.page_importer) self.stack_main.add_titled( child=self.page_list, name="page_list", title=_("Bottles") @@ -237,10 +234,10 @@ def set_manager(result: Manager, error=None): child=self.page_library, name="page_library", title=_("Library") ).set_icon_name("library-symbolic") - self.page_list.search_bar.set_key_capture_widget(self) + self.search_bar.set_key_capture_widget(self) self.btn_search.bind_property( "active", - self.page_list.search_bar, + self.search_bar, "search-mode-enabled", GObject.BindingFlags.BIDIRECTIONAL, ) @@ -261,7 +258,6 @@ def set_manager(result: Manager, error=None): ) self.lock_ui(False) - self.headerbar.get_style_context().remove_class("flat") user_defined_bottles_path = self.manager.data_mgr.get( UserDataKeys.CustomBottlesPath @@ -295,6 +291,16 @@ def get_manager(): self.check_crash_log() + def __search_bottles(self, widget, event=None, data=None): + terms = widget.get_text() + self.page_list.list_bottles.set_filter_func(self.__filter_bottles, terms) + self.page_list.list_steam.set_filter_func(self.__filter_bottles, terms) + + @staticmethod + def __filter_bottles(row, terms=None): + text = row.get_title().lower() + return terms.lower() in text + def send_notification(self, title, text, image="", ignore_user=False): """ This method is used to send a notification to the user using @@ -315,7 +321,7 @@ def go_back(self, *_args): self.main_leaf.navigate(direction=Adw.NavigationDirection.BACK) def show_details_view(self, widget=False, config: BottleConfig | None = None): - self.main_leaf.set_visible_child(self.page_details) + self.main_leaf.push(self.page_details) self.page_details.set_config(config or BottleConfig()) def show_loading_view(self, widget=False): @@ -334,7 +340,7 @@ def show_list_view(self, widget=False): self.stack_main.set_visible_child_name("page_list") def show_importer_view(self, widget=False): - self.main_leaf.set_visible_child(self.page_importer) + self.main_leaf.push(self.page_importer) def show_prefs_view(self, widget=False, view=0): preferences_window = PreferencesWindow(self) @@ -358,13 +364,6 @@ def check_crash_log(self): if crash_log: CrashReportDialog(self, crash_log).present() - def toggle_selection_mode(self, status: bool = True): - context = self.headerbar.get_style_context() - if status: - context.add_class("selection-mode") - else: - context.remove_class("selection-mode") - def lock_ui(self, status: bool = True): widgets = [ self.btn_add,