GSoC Progress Update
In my last blog post, I explained how selection mode was implemented in Games. That was one of the first steps to support Collections in Games, as an efficient way to select games to add/remove from collection is crucial for managing collections. In this post I’ll be talking about how “Favorites Collection” will be implemented in GNOME Games.
The first thing to do was to introduce a
Collection interface to define a behavior that all types of collections must follow.
All collections must have an ID and a title. Apart from that, all collections must provide a way to add and remove games from it. And on adding or removing a game from the collection, it should emit a “game added” or “game removed” signal respectively. A collection must also implement a
load(), which when called, should load the games belonging to a collection from the database. Since there’s going to be different types of collections, how a collection has to be loaded might differ from each other.
Every collection has its own
GameModel and must implement a
GameModel is a
ListModel which stores the list of games in a collection, and
get_game_model() returns its
GameModel which can be bound to the flowbox of a
GamesPage (a widget where games can be displayed with thumbnail and title).
Other than these, all collections must also implement
on_game_replaced(). These are unrelated to games being added or removed to or from a collection. These has to do with games being discovered, and when some games are no longer available to the app. When a game is discovered by tracker or a cached game is loaded, it is added to a
games hash table. This emits a
game_added signal (unrelated to a collection’s
game_added), which every collection listens to. If the added game belongs to the collection, it adds this game to the collection. Similarly
on_game_replaced() handles stuff related to when a game which was cached but is no longer found by the app, and when a game has been renamed, moved to a different directory, or when it’s still the same cached game but with different UID etc.
With the general behavior of a collection defined, it was time to introduce a
FavoritesCollection which implements
As obvious from the name, a “Favorites collection” is a collection that stores games that a user marks as favorite. Favorite games are marked with a star icon on the thumbnail. A game can be added to favorites from the main Games Page or from the Platforms Page. A game can removed from favorites from the above said pages as well as from the Favorites Collection Page. Games are added or removed from favorites “automagically” depending on the list of currently selected games. If all of the selected games are favorite then clicking on the favorite action in the action bar will remove them from favorites. If none of the selected games are favorite, then they are added to favorite. If selected games are a mix of favorite and non favorite games, then all the non favorite games are added to favorites. The icon in the favorite action button in the action bar, dynamically changes from starred to semi-starred to non-starred depending on the selected games. This along with tool tips help users know what the button will do.
Collections: Behind The Scenes
So once a
FavoritesCollection was ready to go, I needed to work on how it will be stored in the database and how it should load those collections.
Favorite games are stored in the database by having a new
is_favorite column in the
games table stores all games, including “manually added” games, and games found by tracker. So adding or removing any type of game from Favorites
is a matter of updating the
is_favorite column in
However, I can’t just simply add a new column in the
games table creation query. In order to migrate from the old database with no
is_favorite column to the new one with the column, I’ll have to give some extra commands to the database. This shouldn’t be done always, but should depend on the version of the database. As of now, there isn’t a database versioning system in Games, so a simple database migration support was quickly implemented. This migration leverages a
.version file in the data directory. This was used to migrate from old data directory structure to a newer one. However the file is empty and data directory migration (not database migration) only checks if the
.version file exists or not. This is fine for a one time migration support. But having versions in the
.version file might come in handy later on, so I now configured the database migration to write the version number into the
.version so it can be used later on. No
.version file is treated as version 0, empty
.version is treated as version 1, and from version 2+, the .
version file will have the version number in it. So in this use case, the database migration to support favorite games should be applied for all versions less than 2. Luckily, migration in this case only required to drop the
games table and create it again with the new
is_favorite column :). And after applying this migration, bump the database version and write it into the
.version file. Now any future migrations may make use of this
.version file too.
The database allows to add, remove, fetch complete list of favorite games, and check if a game is favorite. That’s about it for Favorites. For other types of collections, which will be introduced in the upcoming days, they would mostly be stored in different tables.
As of now, the only available collection is Favorites Collection. But as said, soon there will be “Recently Played” and custom collections that users can create and manage. So when a
CollectionManager is introduced, it has to be designed keeping in mind that it has to handle all types of collections. But most of the collection specific behavior can be neatly abstracted.
CollectionManager handles the creation of all types of collections. It consists of a hash table to store all collections. And as of now, it has a
FavoritesCollection object. Since any actions related to a collection is better to be handled by
CollectionManager it also provides a
favorite_action() which accepts a list of games and depending on it adds or removes them to favorites. Apart from that, on creation of
CollectionManager, it calls
load() on every collection in the hash table, which loads all the games that belongs to that particular collection from the database.
It is a
ListModel that contains all the available collections, which can be bound to the
CollectionsPage (similar to
GamesPage) is a widget which displays available collection to the user with thumbnails and title.
Collections: What You See
With all these put together we get a functional Favorites Collection. Here are some pictures:
As you can see, the notable visual changes here are:
- A new Collections Page which can be navigated to, from the view switcher
- A star on thumbnails of games marked as favorite, in Games Page and Platforms Page
- A collection thumbnail which is generated from the covers of games that belong to that collection
- A Collection (Sub)Page which when clicked opens a page with games that belong to that collection
By the way, you can also see the new game covers with the blurred background that I was experimenting with in the last blog post.
So this is where my progress is at currently. You can see the relevant MR here.
In the upcoming weeks I’ll be implementing the rest of the collections. Currently my plan is to implement “Recently Played” collection next as it would the simpler one to implement next. There are also some smaller stuff which needs some work such as search and selection support for Collections. But that is planned to be at the end, after introducing all types of collection.
The work is going great and all the challenges have been fun to solve so far. Many thanks to my mentor, Alexander for being very helpful as always. Thank you all for reading my blog posts.
See you next time :)