From 13df9d1367929818d750d0442052a533475f7676 Mon Sep 17 00:00:00 2001 From: Daniel Johnson Date: Mon, 2 Jan 2023 09:39:36 -0500 Subject: [PATCH] Use a GtkStack for the LutrisWindow views So, now the scroll bars are updates when you switch. But still allocate each view when first used. There's still some waste here if you switch back and forth between grid and list views, but it should not be too bad. Resolves #3881 --- lutris/gui/lutriswindow.py | 66 +++++++++++++++++------------ lutris/gui/views/grid.py | 22 +++++++--- lutris/gui/views/list.py | 31 ++++++++++---- lutris/gui/widgets/cellrenderers.py | 4 +- share/lutris/ui/lutris-window.ui | 13 ++---- 5 files changed, 81 insertions(+), 55 deletions(-) diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py index a00e8df29..90e8ad445 100644 --- a/lutris/gui/lutriswindow.py +++ b/lutris/gui/lutriswindow.py @@ -49,7 +49,7 @@ class LutrisWindow(Gtk.ApplicationWindow, "view-updated": (GObject.SIGNAL_RUN_FIRST, None, ()), } - games_scrollwindow = GtkTemplate.Child() + games_stack = GtkTemplate.Child() sidebar_revealer = GtkTemplate.Child() sidebar_scrolled = GtkTemplate.Child() game_revealer = GtkTemplate.Child() @@ -86,7 +86,8 @@ class LutrisWindow(Gtk.ApplicationWindow, self.set_service(self.filters.get("service")) self.icon_type = self.load_icon_type() self.game_store = GameStore(self.service, self.service_media) - self.view = Gtk.Box() + self.current_view = Gtk.Box() + self.views = {} self.connect("delete-event", self.on_window_delete) self.connect("configure-event", self.on_window_configure) @@ -97,7 +98,7 @@ class LutrisWindow(Gtk.ApplicationWindow, self.init_template() self._init_actions() - self.set_viewtype_icon(self.view_type) + self.set_viewtype_icon(self.current_view_type) lutris_icon = Gtk.Image.new_from_icon_name("lutris", Gtk.IconSize.MENU) lutris_icon.set_margin_right(3) @@ -195,12 +196,12 @@ class LutrisWindow(Gtk.ApplicationWindow, def on_load(self, widget, data=None): """Finish initializing the view""" self._bind_zoom_adjustment() - self.view.grab_focus() - self.view.contextual_menu = ContextualMenu(self.game_actions.get_game_actions()) + self.current_view.grab_focus() + self.current_view.contextual_menu = ContextualMenu(self.game_actions.get_game_actions()) def on_sidebar_realize(self, widget, data=None): """Grab the initial focus after the sidebar is initialized - so the view is ready.""" - self.view.grab_focus() + self.current_view.grab_focus() def load_filters(self): """Load the initial filters when creating the view""" @@ -448,7 +449,8 @@ class LutrisWindow(Gtk.ApplicationWindow, self.hide_overlay() games = self.get_games_from_filters() logger.debug("Showing %d games", len(games)) - self.view.service = self.service.id if self.service else None + for view in self.views.values(): + view.service = self.service.id if self.service else None GLib.idle_add(self.update_revealer) for game in games: self.game_store.add_game(game) @@ -541,7 +543,7 @@ class LutrisWindow(Gtk.ApplicationWindow, # which keys actually start searching if event.keyval == Gdk.KEY_Escape: self.search_entry.set_text("") - self.view.grab_focus() + self.current_view.grab_focus() return Gtk.ApplicationWindow.do_key_press_event(self, event) if ( # pylint: disable=too-many-boolean-expressions @@ -575,25 +577,33 @@ class LutrisWindow(Gtk.ApplicationWindow, if not self.game_store: logger.error("No game store yet") return - if self.view: - self.view.destroy() self.game_store = GameStore(self.service, self.service_media) - if self.view_type == "grid": - self.view = GameGridView( - self.game_store, - self.game_store.service_media, - hide_text=settings.read_setting("hide_text_under_icons") == "True" - ) - else: - self.view = GameListView(self.game_store, self.game_store.service_media) - self.view.connect("game-selected", self.on_game_selection_changed) - self.view.connect("game-activated", self.on_game_activated) - self.view.contextual_menu = ContextualMenu(self.game_actions.get_game_actions()) - for child in self.games_scrollwindow.get_children(): - child.destroy() - self.games_scrollwindow.add(self.view) - self.view.show_all() + view_type = self.current_view_type + + if view_type in self.views: + self.current_view = self.views[view_type] + self.current_view.set_game_store(self.game_store) + else: + if view_type == "grid": + self.current_view = GameGridView( + self.game_store, + hide_text=settings.read_setting("hide_text_under_icons") == "True" + ) + else: + self.current_view = GameListView(self.game_store) + + self.current_view.connect("game-selected", self.on_game_selection_changed) + self.current_view.connect("game-activated", self.on_game_activated) + self.current_view.contextual_menu = ContextualMenu(self.game_actions.get_game_actions()) + + scrolledwindow = Gtk.ScrolledWindow() + scrolledwindow.add(self.current_view) + scrolledwindow.show_all() + self.games_stack.add_named(scrolledwindow, view_type) + self.views[view_type] = self.current_view + + self.games_stack.set_visible_child_name(view_type) self.update_store() def set_viewtype_icon(self, view_type): @@ -701,14 +711,14 @@ class LutrisWindow(Gtk.ApplicationWindow, def on_search_entry_key_press(self, widget, event): if event.keyval == Gdk.KEY_Down: if self.current_view_type == 'grid': - self.view.select_path(Gtk.TreePath('0')) # needed for gridview only + self.current_view.select_path(Gtk.TreePath('0')) # needed for gridview only # if game_bar is alive at this point it can mess grid item selection up # for some unknown reason, # it is safe to close it here, it will be reopened automatically. if self.game_bar: self.game_bar.destroy() # for gridview only - self.view.set_cursor(Gtk.TreePath('0'), None, False) # needed for both view types - self.view.grab_focus() + self.current_view.set_cursor(Gtk.TreePath('0'), None, False) # needed for both view types + self.current_view.grab_focus() @GtkTemplate.Callback def on_about_clicked(self, *_args): diff --git a/lutris/gui/views/grid.py b/lutris/gui/views/grid.py index ebf5b2a6a..855d3b8b5 100644 --- a/lutris/gui/views/grid.py +++ b/lutris/gui/views/grid.py @@ -13,29 +13,37 @@ class GameGridView(Gtk.IconView, GameView): min_width = 70 # Minimum width for a cell - def __init__(self, store, service_media, hide_text=False): - self.game_store = store - self.service_media = service_media - self.model = self.game_store.store - super().__init__(model=self.game_store.store) + def __init__(self, store, hide_text=False): + super().__init__() GameView.__init__(self) self.service = None self.set_column_spacing(6) self.set_pixbuf_column(COL_ICON) self.set_item_padding(1) - self.cell_width = max(service_media.size[0], self.min_width) if hide_text: self.cell_renderer = None else: - self.cell_renderer = GridViewCellRendererText(self.cell_width) + self.cell_renderer = GridViewCellRendererText() self.pack_end(self.cell_renderer, False) self.add_attribute(self.cell_renderer, "markup", COL_NAME) + self.set_game_store(store) + self.connect_signals() self.connect("item-activated", self.on_item_activated) self.connect("selection-changed", self.on_selection_changed) + def set_game_store(self, game_store): + self.game_store = game_store + self.service_media = game_store.service_media + self.model = game_store.store + self.set_model(self.model) + + if self.cell_renderer: + cell_width = max(game_store.service_media.size[0], self.min_width) + self.cell_renderer.set_width(cell_width) + def select(self): self.select_path(self.current_path) diff --git a/lutris/gui/views/list.py b/lutris/gui/views/list.py index 2eadc0df3..bafa26f88 100644 --- a/lutris/gui/views/list.py +++ b/lutris/gui/views/list.py @@ -21,21 +21,24 @@ class GameListView(Gtk.TreeView, GameView): __gsignals__ = GameView.__gsignals__ - def __init__(self, store, service_media): - self.game_store = store - self.service_media = service_media - self.model = self.game_store.store - super().__init__(model=self.model) + def __init__(self, store): + super().__init__() GameView.__init__(self) + self.set_rules_hint(True) # Icon column if settings.SHOW_MEDIA: image_cell = Gtk.CellRendererPixbuf() - column = Gtk.TreeViewColumn("", image_cell, pixbuf=COL_ICON) - column.set_reorderable(True) - column.set_sort_indicator(False) - self.append_column(column) + self.media_column = Gtk.TreeViewColumn("", image_cell, pixbuf=COL_ICON) + self.media_column.set_reorderable(True) + self.media_column.set_sort_indicator(False) + self.media_column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) + self.append_column(self.media_column) + else: + self.media_column = None + + self.set_game_store(store) # Text columns default_text_cell = self.set_text_cell() @@ -59,6 +62,16 @@ class GameListView(Gtk.TreeView, GameView): self.connect("row-activated", self.on_row_activated) self.get_selection().connect("changed", self.on_cursor_changed) + def set_game_store(self, game_store): + self.game_store = game_store + self.service_media = game_store.service_media + self.model = game_store.store + self.set_model(self.model) + + if self.media_column: + media_width = game_store.service_media.size[0] + self.media_column.set_fixed_width(media_width) + @staticmethod def set_text_cell(): text_cell = Gtk.CellRendererText() diff --git a/lutris/gui/widgets/cellrenderers.py b/lutris/gui/widgets/cellrenderers.py index a0aec0a46..23f294707 100644 --- a/lutris/gui/widgets/cellrenderers.py +++ b/lutris/gui/widgets/cellrenderers.py @@ -4,10 +4,12 @@ from gi.repository import Gtk, Pango class GridViewCellRendererText(Gtk.CellRendererText): """CellRendererText adjusted for grid view display, removes extra padding""" - def __init__(self, width, *args, **kwargs): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.props.alignment = Pango.Alignment.CENTER self.props.wrap_mode = Pango.WrapMode.WORD self.props.xalign = 0.5 self.props.yalign = 0 + + def set_width(self, width): self.props.wrap_width = width diff --git a/share/lutris/ui/lutris-window.ui b/share/lutris/ui/lutris-window.ui index f927c79df..391cc9211 100644 --- a/share/lutris/ui/lutris-window.ui +++ b/share/lutris/ui/lutris-window.ui @@ -76,18 +76,11 @@ True False - + True - True - True + False - - True - False - - - - +