Repository: lutris/lutris

Issue:
Add a "Return to main library" button when searching on Lutris.net
The only way to return to main game library right now is by unselecting the game in the right panel first (by clicking an on X button) and only then, by clicking on a very tiny and hard to notice button in the search box,  you can return to the main library with all your games.
This process is just EXTREMELY annoying and completely unintuitive, so to avoid that, I suggest adding a single BIG button in left panel that groups all these actions.


Discussion:
I would highly suggest to implement this in 0.5.1 as it's one of the biggest complaints about new Lutris version at the moment.

Resolving patch:
diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py
--- a/lutris/gui/lutriswindow.py
+++ b/lutris/gui/lutriswindow.py
@@ -65,6 +65,7 @@ class LutrisWindow(Gtk.ApplicationWindow):
     sync_spinner = GtkTemplate.Child()
     add_popover = GtkTemplate.Child()
     viewtype_icon = GtkTemplate.Child()
+    website_search_toggle = GtkTemplate.Child()
 
     def __init__(self, application, **kwargs):
         width = int(settings.read_setting("width") or self.default_width)
@@ -94,13 +95,14 @@ def __init__(self, application, **kwargs):
         self.game_actions = GameActions(application=application, window=self)
 
         self.search_terms = None
+        self.search_timer_id = None
+        self.search_mode = "local"
         self.game_store = self.get_store()
         self.view = self.get_view(view_type)
 
         GObject.add_emission_hook(Game, "game-updated", self.on_game_updated)
         GObject.add_emission_hook(Game, "game-removed", self.on_game_updated)
         GObject.add_emission_hook(Game, "game-installed", self.on_game_installed)
-        GObject.add_emission_hook(GenericPanel, "game-searched", self.on_game_searched)
         GObject.add_emission_hook(GenericPanel,
                                   "running-game-selected",
                                   self.game_selection_changed)
@@ -119,6 +121,11 @@ def __init__(self, application, **kwargs):
         self.set_viewtype_icon(view_type)
 
         # Add additional widgets
+        lutris_icon = Gtk.Image.new_from_icon_name("lutris", Gtk.IconSize.MENU)
+        lutris_icon.set_margin_right(10)
+        lutris_icon.set_margin_left(10)
+        self.website_search_toggle.set_image(lutris_icon)
+        self.website_search_toggle.set_tooltip_text("Search on Lutris.net")
         self.sidebar_listbox = SidebarListBox()
         self.sidebar_listbox.set_size_request(250, -1)
         self.sidebar_listbox.connect("selected-rows-changed", self.on_sidebar_changed)
@@ -403,7 +410,7 @@ def do_key_press_event(self, event):
         if event.keyval == Gdk.KEY_Escape:
             self.search_toggle.set_active(False)
             return Gdk.EVENT_STOP
-        return Gtk.ApplicationWindow.do_key_press_event(self, event)
+        # return Gtk.ApplicationWindow.do_key_press_event(self, event)
 
         # XXX: This block of code below is to enable searching on type.
         # Enabling this feature steals focus from other entries so it needs
@@ -411,19 +418,19 @@ def do_key_press_event(self, event):
 
         # Probably not ideal for non-english, but we want to limit
         # which keys actually start searching
-        # if (
-        #     not Gdk.KEY_0 <= event.keyval <= Gdk.KEY_z
-        #     or event.state & Gdk.ModifierType.CONTROL_MASK
-        #     or event.state & Gdk.ModifierType.SHIFT_MASK
-        #     or event.state & Gdk.ModifierType.META_MASK
-        #     or event.state & Gdk.ModifierType.MOD1_MASK
-        #     or self.search_entry.has_focus()
-        # ):
-        #     return Gtk.ApplicationWindow.do_key_press_event(self, event)
-
-        # self.search_toggle.set_active(True)
-        # self.search_entry.grab_focus()
-        # return self.search_entry.do_key_press_event(self.search_entry, event)
+        if (
+            not Gdk.KEY_0 <= event.keyval <= Gdk.KEY_z
+            or event.state & Gdk.ModifierType.CONTROL_MASK
+            or event.state & Gdk.ModifierType.SHIFT_MASK
+            or event.state & Gdk.ModifierType.META_MASK
+            or event.state & Gdk.ModifierType.MOD1_MASK
+            or self.search_entry.has_focus()
+        ):
+            return Gtk.ApplicationWindow.do_key_press_event(self, event)
+
+        self.search_toggle.set_active(True)
+        self.search_entry.grab_focus()
+        return self.search_entry.do_key_press_event(self.search_entry, event)
 
     def load_icon_type_from_settings(self, view_type):
         """Return the icon style depending on the type of view."""
@@ -635,19 +642,40 @@ def set_show_installed_state(self, filter_installed):
         self.invalidate_game_filter()
 
     @GtkTemplate.Callback
-    def on_search_entry_changed(self, widget):
+    def on_search_entry_changed(self, entry):
         """Callback for the search input keypresses"""
-        self.game_store.filter_text = widget.get_text()
-        self.invalidate_game_filter()
+        if self.search_mode == "local":
+            self.game_store.filter_text = entry.get_text()
+            self.invalidate_game_filter()
+        elif self.search_mode == "website":
+            if self.search_timer_id:
+                GLib.source_remove(self.search_timer_id)
+            self.search_timer_id = GLib.timeout_add(
+                750, self.on_search_games_fire, entry.get_text().lower().strip()
+            )
+        else:
+            raise ValueError("Unsupported search mode %s" % self.search_mode)
 
     @GtkTemplate.Callback
     def on_search_toggle(self, button):
+        """Called when search bar is shown / hidden"""
         active = button.props.active
         self.search_revealer.set_reveal_child(active)
-        if not active:
+        if active:
+            self.search_entry.grab_focus()
+        else:
             self.search_entry.props.text = ""
+
+    @GtkTemplate.Callback
+    def on_website_search_toggle_toggled(self, toggle_button):
+        self.search_terms = self.search_entry.props.text
+        if toggle_button.props.active:
+            self.search_mode = "website"
+            self.game_store.search_mode = True
+            self.search_games(self.search_terms)
         else:
-            self.search_entry.grab_focus()
+            self.search_mode = "local"
+            self.search_games("")
 
     @GtkTemplate.Callback
     def on_about_clicked(self, *_args):
@@ -673,17 +701,23 @@ def on_game_updated(self, game):
         self.sidebar_listbox.update()
         return True
 
-    def on_game_searched(self, panel, query):
-        """Called when the game-searched event is emitted"""
-        # logger.info("Searching for :%s" % query)
+    def on_search_games_fire(self, value):
+        GLib.source_remove(self.search_timer_id)
+        self.search_timer_id = None
+        self.search_games(value)
+        return False
+
+    def search_games(self, query):
+        """Search for games from the website API"""
+        logger.debug("%s search for :%s", self.search_mode, query)
         self.search_terms = query
         self.view.destroy()
         self.game_store = self.get_store(api.search_games(query) if query else None)
         self.game_store.set_icon_type(self.icon_type)
         self.game_store.load(from_search=bool(query))
+        self.game_store.filter_text = self.search_entry.props.text
         self.switch_view(self.get_view_type())
         self.invalidate_game_filter()
-        return True
 
     def game_selection_changed(self, _widget, game):
         """Callback to handle the selection of a game in the view"""
@@ -693,10 +727,7 @@ def game_selection_changed(self, _widget, game):
             child.destroy()
 
         if not game:
-            self.game_panel = GenericPanel(
-                search_terms=self.search_terms,
-                application=self.application
-            )
+            self.game_panel = GenericPanel(application=self.application)
         else:
             self.game_actions.set_game(game=game)
             self.game_panel = GamePanel(self.game_actions)
diff --git a/lutris/gui/views/generic_panel.py b/lutris/gui/views/generic_panel.py
--- a/lutris/gui/views/generic_panel.py
+++ b/lutris/gui/views/generic_panel.py
@@ -32,14 +32,12 @@ class GenericPanel(Gtk.Fixed):
 
     __gtype_name__ = "LutrisPanel"
     __gsignals__ = {
-        "game-searched": (GObject.SIGNAL_RUN_FIRST, None, (str, )),
         "running-game-selected": (GObject.SIGNAL_RUN_FIRST, None, (Game, ))
     }
 
-    def __init__(self, search_terms=None, application=None):
+    def __init__(self, application=None):
         super().__init__(visible=True)
         self.application = application
-        self.search_terms = search_terms or ""
         self.set_size_request(320, -1)
         self.get_style_context().add_class("game-panel")
         self.set_background()
@@ -153,31 +151,12 @@ def get_user_info_box(self):
         return user_box
 
     def get_lutris_links(self):
-        search_entry = Gtk.SearchEntry(visible=True)
         box = Gtk.VBox(spacing=6, visible=True)
-        floss_button = get_link_button("Browse Open Source games")
-        floss_button.connect("clicked", lambda *x: search_entry.set_text("open source games"))
-        box.add(floss_button)
-
-        f2p_button = get_link_button("Browse Free to Play games")
-        f2p_button.connect("clicked", lambda *x: search_entry.set_text("free to play games"))
-        box.add(f2p_button)
 
         donate_button = get_link_button("Support the project")
         donate_button.connect("clicked", lambda *x: open_uri(LINKS["donate"]))
         box.add(donate_button)
 
-        search_label = Gtk.Label(visible=True)
-        search_label.set_markup("<b>Search games on Lutris.net:</b>")
-        search_label.set_alignment(0, 0.5)
-        search_label.set_margin_top(12)
-        box.add(search_label)
-
-        search_entry = Gtk.SearchEntry(visible=True)
-        search_entry.set_text(self.search_terms)
-        search_entry.connect("changed", self.on_search_entry_changed)
-        box.add(search_entry)
-
         help_label = Gtk.Label(visible=True)
         help_label.set_markup("<b>Help:</b>")
         help_label.set_alignment(0, 0.5)
@@ -214,16 +193,3 @@ def on_running_game_select(self, widget, row):
         else:
             game = row.get_children()[0].game
         self.emit("running-game-selected", game)
-
-    def on_search_entry_changed(self, entry):
-        if self.timer_id:
-            GLib.source_remove(self.timer_id)
-        self.timer_id = GLib.timeout_add(
-            750, self.search_games, entry.get_text().lower().strip()
-        )
-
-    def search_games(self, value):
-        self.emit("game-searched", value)
-        GLib.source_remove(self.timer_id)
-        self.timer_id = None
-        return False


Test patch: