Repository: lutris/lutris

Issue:
Suggestion: Option to sort installed games first.
Under the View menu there should be a checkbox to sort installed games before not installed games, like this:
![lutris_installed_first](https://user-images.githubusercontent.com/31285258/33522198-81ec185e-d7e6-11e7-9ad9-8c42d6f9bd89.png)



Discussion:

Resolving patch:
diff --git a/lutris/gui/gameviews.py b/lutris/gui/gameviews.py
--- a/lutris/gui/gameviews.py
+++ b/lutris/gui/gameviews.py
@@ -47,18 +47,23 @@ class GameStore(GObject.Object):
         "icons-changed": (GObject.SIGNAL_RUN_FIRST, None, (str,))
     }
 
-    def __init__(self, games, icon_type, filter_installed):
+    def __init__(self, games, icon_type, filter_installed, show_installed_first=False):
         super(GameStore, self).__init__()
         self.games = games
         self.icon_type = icon_type
         self.filter_installed = filter_installed
+        self.show_installed_first = show_installed_first
         self.filter_text = None
         self.filter_runner = None
         self.filter_platform = None
+        self.modelfilter = None
         self.runner_names = {}
 
         self.store = Gtk.ListStore(int, str, str, Pixbuf, str, str, str, str, int, str, bool, int, str)
-        self.store.set_sort_column_id(COL_NAME, Gtk.SortType.ASCENDING)
+        if show_installed_first:
+            self.store.set_sort_column_id(COL_INSTALLED, Gtk.SortType.DESCENDING)
+        else:
+            self.store.set_sort_column_id(COL_NAME, Gtk.SortType.ASCENDING)
         self.modelfilter = self.store.filter_new()
         self.modelfilter.set_visible_func(self.filter_view)
         if games:
@@ -111,6 +116,15 @@ def filter_view(self, model, _iter, filter_data=None):
                 return False
         return True
 
+    def sort_view(self, show_installed_first=False):
+        self.show_installed_first = show_installed_first
+        if show_installed_first:
+            self.store.set_sort_column_id(COL_INSTALLED, Gtk.SortType.DESCENDING)
+            self.modelfilter.get_model().set_sort_column_id(COL_INSTALLED, Gtk.SortType.DESCENDING)
+        else:
+            self.store.set_sort_column_id(COL_NAME, Gtk.SortType.ASCENDING)
+            self.modelfilter.get_model().set_sort_column_id(COL_NAME, Gtk.SortType.ASCENDING)
+
     def add_game_by_id(self, game_id):
         """Add a game into the store."""
         if not game_id:
diff --git a/lutris/gui/lutriswindow.py b/lutris/gui/lutriswindow.py
--- a/lutris/gui/lutriswindow.py
+++ b/lutris/gui/lutriswindow.py
@@ -80,6 +80,8 @@ def __init__(self, application, **kwargs):
         self.load_icon_type_from_settings(view_type)
         self.filter_installed = \
             settings.read_setting('filter_installed') == 'true'
+        self.show_installed_first = \
+            settings.read_setting('show_installed_first') == 'true'
         self.sidebar_visible = \
             settings.read_setting('sidebar_visible') in ['true', None]
         self.use_dark_theme = settings.read_setting('dark_theme') == 'true'
@@ -89,8 +91,8 @@ def __init__(self, application, **kwargs):
             service.sync_with_lutris()
 
         # Window initialization
-        self.game_list = pga.get_games()
-        self.game_store = GameStore([], self.icon_type, self.filter_installed)
+        self.game_list = pga.get_games(show_installed_first=self.show_installed_first)
+        self.game_store = GameStore([], self.icon_type, self.filter_installed, self.show_installed_first)
         self.view = self.get_view(view_type)
         super().__init__(default_width=width,
                          default_height=height,
@@ -188,6 +190,8 @@ def _init_actions(self):
             'show-installed-only': Action(self.on_show_installed_state_change, type='b',
                                           default=self.filter_installed,
                                           accel='<Primary>h'),
+            'show-installed-first': Action(self.on_show_installed_first_state_change, type='b',
+                                           default=self.show_installed_first),
             'view-type': Action(self.on_viewtype_state_change, type='s',
                                 default=self.current_view_type),
             'icon-type': Action(self.on_icontype_state_change, type='s',
@@ -496,6 +500,20 @@ def on_preferences_activate(self, *args):
         """Callback when preferences is activated."""
         SystemConfigDialog(parent=self)
 
+    def on_show_installed_first_state_change(self, action, value):
+        action.set_state(value)
+        show_installed_first = value.get_boolean()
+        self.set_show_installed_first_state(show_installed_first)
+
+    def set_show_installed_first_state(self, show_installed_first):
+        self.show_installed_first = show_installed_first
+        setting_value = 'true' if show_installed_first else 'false'
+        settings.write_setting(
+            'show_installed_first', setting_value
+        )
+        self.game_store.sort_view(show_installed_first)
+        self.game_store.modelfilter.refilter()
+
     def on_show_installed_state_change(self, action, value):
         action.set_state(value)
         filter_installed = value.get_boolean()
diff --git a/lutris/pga.py b/lutris/pga.py
--- a/lutris/pga.py
+++ b/lutris/pga.py
@@ -127,7 +127,7 @@ def set_config_paths():
             )
 
 
-def get_games(name_filter=None, filter_installed=False, filter_runner=None, select='*'):
+def get_games(name_filter=None, filter_installed=False, filter_runner=None, select='*', show_installed_first=False):
     """Get the list of every game in database."""
     query = "select " + select + " from games"
     params = []
@@ -142,7 +142,10 @@ def get_games(name_filter=None, filter_installed=False, filter_runner=None, sele
         filters.append("runner = ?")
     if filters:
         query += " WHERE " + " AND ".join([f for f in filters])
-    query += " ORDER BY slug"
+    if show_installed_first:
+        query += " ORDER BY installed DESC, slug"
+    else:
+        query += " ORDER BY slug"
 
     return sql.db_query(PGA_DB, query, tuple(params))
 


Test patch: